diff options
346 files changed, 40557 insertions, 8599 deletions
diff --git a/.gitmodules b/.gitmodules index 473a21289..c85c089b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "fpga-src"] path = fpga-src url = https://github.com/EttusResearch/fpga.git - branch = maint + branch = master @@ -1,6 +1,48 @@ Change Log for Releases ============================== +## 003.009.000 +* X300: Updated DAC ctrl, FPGA toolchain is now entirely Vivado, + improved master clock controls, added ADC self-cal capability, + prepared for revisions 7 and 8, fixed flow control issue which + could cause device to hang when receiving too many overruns +* B2XX: Auto clock rate setting, added PID/VID pairs to support + all B2XX- and derivatives, added temperature sensor, improved + DC offset and IQ imbalance correction, added AGC support, + support for FPGPIO connector on Rev6+ boards, full clock range support, + updated FX3 firmware (side-channel logging capabilities, updated + tx voltage swing, better configurability), default tick rate now + 16 MHz, added B200mini support +* E3XX: Added temperature sensor, FPGA toolchain is now entirely Vivado, + improved DC offset and IQ imbalance correction, added AGC support, + improved FPGA capture interface robustness for RFIC, make frame + sizes configurable, replaced GPS control code with gpsd interfacing + capabilities +* Octoclock: Fixed bootloader + ethernet capabilities +* Compilers: Supported MSVC versions are now 2012, 2013, 2015 + (dropped 2010 support), added MinGW capabilities +* Documentation: Many minor fixes and updates, merged all the + info from code.ettus.com +* UHD: Added sid_t, CHDR-specific transports now get their own + (un)packer codes, fixed a lot of compiler warnings, added + filter API (currently available for AD9361 frontend), added + soft-register API, replaced Cheetah with Mako, full Py3k + compliance, updated images downloader tool (now is one tool + for all devices), CMake minimum version is now 2.8, refactored + general AD9361 peripheral management, refactored most core + control management, added usb_error type (used by B2xx devices), + better exception handling at runtime, added C wrapper API, + new dependency: python-requests +* C API: Added to UHD (wraps C++ calls in C) +* multi_usrp: Added normalized gain setters/getters, IQ imbalance + + DC offset correction API, filter API +* Converters: Converter symbols now exported, better logging, + removed ORC dependency, added u8 converters +* Examples: Whitespace- and other cleanup, multi-channel fixes for + some examples +* Utils: Read more property tree types from the command line +* Tools: kitchen sink updated, added mega_fft + ## 003.008.005 * E3xx: Added support for battery-based E3xx device (E312), get_freq() for network mode, fixed GPS time initialization bug diff --git a/firmware/fx3/b200/b200_gpifconfig.h b/firmware/fx3/b200/b200_gpifconfig.h index 58836fac8..a6a24e0c5 100644..100755 --- a/firmware/fx3/b200/b200_gpifconfig.h +++ b/firmware/fx3/b200/b200_gpifconfig.h @@ -1,10 +1,6 @@ -//
-// Copyright 2013-2014 Ettus Research LLC
-//
-
/*
- * Project Name: b200_v2.cyfx
- * Time : 01/17/2013 12:50:08
+ * Project Name: ssf2-edit.cyfx
+ * Time : 11/17/2014 13:01:01
* Device Type: FX3
* Project Type: GPIF2
*
@@ -13,7 +9,7 @@ *
* This is a generated file and should not be modified
* This file need to be included only once in the firmware
- * This file is generated by Gpif2 designer tool version - 1.0.715.0
+ * This file is generated by Gpif2 designer tool version - 1.0.837.1
*
*/
@@ -25,7 +21,7 @@ /* Summary
Number of states in the state machine
*/
-#define CY_NUMBER_OF_STATES 6
+#define CY_NUMBER_OF_STATES 7
/* Summary
Mapping of user defined state names to state indices
@@ -36,12 +32,13 @@ #define WRITE 3
#define SHORT_PKT 4
#define ZLP 5
+#define DSS_STATE 6
/* Summary
Initial value of early outputs from the state machine.
*/
-#define ALPHA_RESET 0x8
+#define ALPHA_RESET 0xC
/* Summary
@@ -58,26 +55,27 @@ uint16_t CyFxGpifTransition[] = { waveform table.
*/
CyU3PGpifWaveData CyFxGpifWavedata[] = {
- {{0x1E086001,0x000100C4,0x80000000},{0x00000000,0x00000000,0x00000000}},
- {{0x4E080302,0x00000200,0x80000000},{0x00000000,0x00000000,0x00000000}},
- {{0x1E086001,0x000100C4,0x80000000},{0x4E040704,0x20000200,0xC0100000}},
+ {{0x1E086001,0x000302C4,0x80000000},{0x00000000,0x00000000,0x00000000}},
+ {{0x4E080302,0x00000300,0x80000000},{0x1E086006,0x000302C4,0x80000000}},
+ {{0x1E086001,0x000302C4,0x80000000},{0x4E040704,0x20000300,0xC0100000}},
+ {{0x4E080302,0x00000300,0x80000000},{0x1E086001,0x000302C4,0x80000000}},
{{0x00000000,0x00000000,0x00000000},{0x00000000,0x00000000,0x00000000}},
- {{0x00000000,0x00000000,0x00000000},{0x3E738705,0x00000200,0xC0100000}},
- {{0x00000000,0x00000000,0x00000000},{0x5E002703,0x2001020C,0x80000000}},
- {{0x00000000,0x00000000,0x00000000},{0x4E040704,0x20000200,0xC0100000}}
+ {{0x00000000,0x00000000,0x00000000},{0x3E738705,0x00000300,0xC0100000}},
+ {{0x00000000,0x00000000,0x00000000},{0x5E002703,0x2003030C,0x80000000}},
+ {{0x00000000,0x00000000,0x00000000},{0x4E040704,0x20000300,0xC0100000}}
};
/* Summary
Table that maps state indices to the descriptor table indices.
*/
uint8_t CyFxGpifWavedataPosition[] = {
- 0,1,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 0,4,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 0,5,0,2,0,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
- 0,6,0,2,0,0
+ 0,1,0,2,0,0,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 0,5,0,2,0,0,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 0,6,0,2,0,0,6,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 0,7,0,2,0,0,7
};
/* Summary
@@ -154,12 +152,12 @@ uint32_t CyFxGpifRegValue[] = { 0x00000000, /* CY_U3P_PIB_GPIF_LAMBDA_STAT */
0x00000000, /* CY_U3P_PIB_GPIF_ALPHA_STAT */
0x00000000, /* CY_U3P_PIB_GPIF_BETA_STAT */
- 0x00080000, /* CY_U3P_PIB_GPIF_WAVEFORM_CTRL_STAT */
+ 0x000C0000, /* CY_U3P_PIB_GPIF_WAVEFORM_CTRL_STAT */
0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH */
0x00000000, /* CY_U3P_PIB_GPIF_WAVEFORM_SWITCH_TIMEOUT */
0x00000000, /* CY_U3P_PIB_GPIF_CRC_CONFIG */
0x00000000, /* CY_U3P_PIB_GPIF_CRC_DATA */
- 0xFFFFFFF1 /* CY_U3P_PIB_GPIF_BETA_DEASSERT */
+ 0xFFFFFFC1 /* CY_U3P_PIB_GPIF_BETA_DEASSERT */
};
/* Summary
diff --git a/firmware/fx3/b200/b200_main.c b/firmware/fx3/b200/b200_main.c index cb0172af3..e552d3177 100644 --- a/firmware/fx3/b200/b200_main.c +++ b/firmware/fx3/b200/b200_main.c @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // /* This file defines the application that runs on the Cypress FX3 device, and @@ -34,18 +34,19 @@ * Indented features must have the parent feature enabled as well. */ -//#define HAS_HEAP // This requires memory to be set aside for the heap (e.g. required for printing floating-point numbers). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to create one. -//#define ENABLE_MSG // This will cause the compiled code to exceed the default text memory area (SYS_MEM). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to resize the memory map so it will fit. -//#define ENABLE_MANUAL_DMA_XFER -//#define ENABLE_MANUAL_DMA_XFER_FROM_HOST -//#define ENABLE_MANUAL_DMA_XFER_TO_HOST +#define HAS_HEAP // This requires memory to be set aside for the heap (e.g. required for printing floating-point numbers). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to create one. +#define ENABLE_MSG // This will cause the compiled code to exceed the default text memory area (SYS_MEM). You can apply the accompanying patch ('fx3_mem_map.patch') to fx3.ld & cyfxtx.c to resize the memory map so it will fit. +#define ENABLE_MANUAL_DMA_XFER // Allows us to set it from the host (using the side channel util), doesn't auto-enable it +//#define ENABLE_P2U_SUSP_EOP +#define ENABLE_MANUAL_DMA_XFER_FROM_HOST +#define ENABLE_MANUAL_DMA_XFER_TO_HOST //#define ENABLE_DMA_BUFFER_PACKET_DEBUG -//#define ENABLE_FPGA_SB // Be careful: this will add an ever-so-slight delay to some operations (e.g. AD3961 tune) +#define ENABLE_FPGA_SB // Be careful: this will add an ever-so-slight delay to some operations (e.g. AD3961 tune) #define ENABLE_RE_ENUM_THREAD #define ENABLE_USB_EVENT_LOGGING //#define PREVENT_LOW_POWER_MODE -//#define ENABLE_INIT_B_WORKAROUND // This should only be enabled if you have a board where the FPGA INIT_B line is broken, but the FPGA is known to work -//#define ENABLE_DONE_WORKAROUND // This should only be enabled if you have a board where the FPGA DONE line is broken, but the FPGA is known to work +/*#define ENABLE_INIT_B_WORKAROUND // This should only be enabled if you have a board where the FPGA INIT_B line is broken, but the FPGA is known to work*/ +/*#define ENABLE_DONE_WORKAROUND // This should only be enabled if you have a board where the FPGA DONE line is broken, but the FPGA is known to work*/ #define WATCHDOG_TIMEOUT 1500 #define CHECK_POWER_STATE_SLEEP_TIME 500 // Should be less than WATCHDOG_TIMEOUT @@ -178,7 +179,7 @@ static uint8_t g_usb_event_log_contiguous_buf[USB_EVENT_LOG_SIZE]; #ifdef ENABLE_FPGA_SB static CyBool_t g_fpga_sb_enabled = CyFalse; -static uint16_t g_fpga_sb_uart_div = 434*2; +//static uint16_t g_fpga_sb_uart_div = 434*2; static uint16_t g_fpga_sb_last_usb_event_log_index = 0; static CyU3PThread thread_fpga_sb_poll; static CyU3PMutex g_suart_lock; @@ -215,7 +216,8 @@ enum ConfigFlags { CF_DMA_BUFFER_SIZE = 1 << 5, CF_DMA_BUFFER_COUNT = 1 << 6, CF_MANUAL_DMA = 1 << 7, - + CF_SB_BAUD_DIV = 1 << 8, + CF_RE_ENUM = 1 << 31 }; @@ -236,7 +238,17 @@ typedef struct ConfigMod { CONFIG config; } CONFIG_MOD, *PCONFIG_MOD; -static CONFIG g_config; +static CONFIG g_config = { + 65, // tx_swing + 0x11, // tx_deemphasis + 0, // disable_usb2 + 1, // enable_as_superspeed + CY_U3P_DS_THREE_QUARTER_STRENGTH, // pport_drive_strength + 64512, // dma_buffer_size 2**16-1, then aligned to next page boundary + 1, // dma_buffer_count + 0, // manual_dma + 434*2 // sb_baud_div +}; static CONFIG_MOD g_config_mod; #define REG_LNK_PHY_ERROR_STATUS 0xE0033044 @@ -251,7 +263,7 @@ enum PhyErrors { PHYERR_PHY_ERROR_EB_UND_EV = 1 << 2, PHYERR_PHY_ERROR_EB_OVR_EV = 1 << 1, PHYERR_PHY_ERROR_DECODE_EV = 1 << 0, - + PHYERR_MAX = PHYERR_PHY_LOCK_EV, PHYERR_MASK = (PHYERR_MAX << 1) - 1 }; @@ -259,7 +271,7 @@ enum PhyErrors { typedef struct USBErrorCounters { int phy_error_count; int link_error_count; - + int PHY_LOCK_EV; int TRAINING_ERROR_EV; int RX_ERROR_CRC32_EV; @@ -281,35 +293,45 @@ typedef struct DMACounters { int ERROR; int PROD_SUSP; int CONS_SUSP; - + int BUFFER_MARKER; int BUFFER_EOP; int BUFFER_ERROR; int BUFFER_OCCUPIED; - + int last_count; int last_size; - + int last_sid; int bad_sid_count; } DMA_COUNTERS, *PDMA_COUNTERS; +typedef struct PIBCounters +{ + int socket_inactive; // CYU3P_PIB_ERR_THR1_SCK_INACTIVE +} PIB_COUNTERS, *PPIB_COUNTERS; + typedef struct Counters { int magic; - + DMA_COUNTERS dma_to_host; DMA_COUNTERS dma_from_host; - + int log_overrun_count; - + int usb_error_update_count; - USB_ERROR_COUNTERS usb_error_counters; - + USB_ERROR_COUNTERS usb_error_counters; + int usb_ep_underrun_count; - + int heap_size; - + int resume_count; + + int state_transition_count; + int invalid_gpif_state; + + PIB_COUNTERS pib_counters[4]; } COUNTERS, *PCOUNTERS; volatile static COUNTERS g_counters; @@ -333,7 +355,7 @@ caddr_t _sbrk(int incr) extern char __heap_end; char *prev_heap_end; - if (heap_end == 0) + if (heap_end == 0) { heap_end = (char *)&__heap_start; } @@ -366,15 +388,15 @@ void msg(const char* str, ...) { va_list args; static char buf[LOG_BUFFER_SIZE]; int idx = 0; - + msg_CHECK_USE_LOCK LOCK(g_log_lock); - + ++log_count; log_count %= 10000; va_start(args, str); - + if (1) { // FIXME: Optional uint32_t time_now = CyU3PGetTime(); idx += sprintf(buf, "%08X %04i ", (uint)time_now, log_count); @@ -382,19 +404,19 @@ void msg(const char* str, ...) { else idx += sprintf(buf, "%04i ", log_count); idx += vsnprintf(buf + idx, LOG_BUFFER_SIZE - idx, str, args); - + va_end(args); - + if ((LOG_BUFFER_SIZE - log_buffer_len) < (idx + 1 + 1)) { msg_CHECK_USE_LOCK LOCK(g_counters_lock); ++g_counters.log_overrun_count; msg_CHECK_USE_LOCK UNLOCK(g_counters_lock); - + goto msg_exit; } - + // Circular buffer if we need it later, but currently won't wrap due to above condition memcpy(log_buffer + log_buffer_len, buf, min(idx + 1, LOG_BUFFER_SIZE - log_buffer_len)); if ((idx + 1) > (LOG_BUFFER_SIZE - log_buffer_len)) @@ -404,7 +426,7 @@ void msg(const char* str, ...) { } else log_buffer[log_buffer_len + idx + 1] = '\0'; - + log_buffer_len += (idx + 1); msg_exit: msg_CHECK_USE_LOCK @@ -437,42 +459,42 @@ void msg_nl(const char* str, ...) */ void log_reset(void) { //LOCK(g_log_lock); - + log_buffer_idx = 0; - log_buffer_len = 0; + log_buffer_len = 0; log_buffer[0] = '\0'; - + //UNLOCK(g_log_lock); } void counters_auto_reset(void) { //LOCK(g_counters_lock); - - g_counters.log_overrun_count = 0; - + + g_counters.log_overrun_count = 0; + //UNLOCK(g_counters_lock); } void counters_dma_reset(void) { LOCK(g_counters_lock); - + LOCK(g_counters_dma_to_host_lock); memset((void*)&g_counters.dma_to_host, 0x00, sizeof(DMA_COUNTERS)); UNLOCK(g_counters_dma_to_host_lock); - + LOCK(g_counters_dma_from_host_lock); memset((void*)&g_counters.dma_from_host, 0x00, sizeof(DMA_COUNTERS)); UNLOCK(g_counters_dma_from_host_lock); - + UNLOCK(g_counters_lock); } void counters_reset_usb_errors(void) { LOCK(g_counters_lock); - + g_counters.usb_error_update_count = 0; memset((void*)&g_counters.usb_error_counters, 0x00, sizeof(g_counters.usb_error_counters)); - + UNLOCK(g_counters_lock); } @@ -485,10 +507,10 @@ void dma_callback ( int from_host) { CyU3PReturnStatus_t status = CY_U3P_SUCCESS; - + PDMA_COUNTERS cnt = (PDMA_COUNTERS)(from_host ? &g_counters.dma_from_host : &g_counters.dma_to_host); CyU3PMutex* lock = (from_host ? &g_counters_dma_from_host_lock : &g_counters_dma_to_host_lock); - + uint16_t buffer_status = (input->buffer_p.status & CY_U3P_DMA_BUFFER_STATUS_MASK); if (buffer_status & CY_U3P_DMA_BUFFER_MARKER) { @@ -513,49 +535,51 @@ void dma_callback ( LOCKP(lock); int prod_cnt = cnt->PROD_EVENT++; UNLOCKP(lock); - - if (cnt->last_count != input->buffer_p.count) - msg("[DMA %05d] buffer.count (%d) != last_count (%d)", prod_cnt, input->buffer_p.count, cnt->last_count); + + //if (cnt->last_count != input->buffer_p.count) + // msg("[DMA%d %05d] buffer.count (%05d) != last_count (%05d)", from_host, prod_cnt, input->buffer_p.count, cnt->last_count); cnt->last_count = input->buffer_p.count; - + if (cnt->last_size != input->buffer_p.size) - msg("[DMA %05d] buffer.size (%d) != last_size (%d)", prod_cnt, input->buffer_p.size, cnt->last_size); + msg("[DMA%d %05d] buffer.size (%05d) != last_size (%05d)", from_host, prod_cnt, input->buffer_p.size, cnt->last_size); cnt->last_size = input->buffer_p.size; - + uint32_t* p32 = input->buffer_p.buffer; uint32_t sid = p32[1]; cnt->last_sid = (int)sid; - if ((sid != 0xa0) && (sid != 0xb0)) + if (((from_host == 0) && ((sid != 0xa0) && (sid != 0xb0))) || + ((from_host == 1) && ((sid != 0x50) && (sid != 0x60)))) { cnt->bad_sid_count++; - msg("[DMA %05d] Bad SID: 0x%08x", prod_cnt, sid); + msg("[DMA%d %05d] Bad SID: 0x%08x", from_host, prod_cnt, sid); } - + uint16_t* p16 = input->buffer_p.buffer; - + if (p32[0] & (((uint32_t)1) << 31)) { - msg("[DMA %05d] Error code: 0x%x (packet len: %d)", prod_cnt, p32[4], p16[0]); // Status - - //msg("[DMA] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); + msg("[DMA%d %05d] Error code: 0x%x (packet len: %05d, buffer count: %05d, seq: %04d)", from_host, prod_cnt, p32[4], p16[0], input->buffer_p.count, (p16[1] & 0x0fff)); // Status + + //msg("[DMA%d] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", from_host, p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); } else - { + { if (p16[1] & (((uint16_t)1) << 12)) { - msg("[DMA %05d] EOB", prod_cnt); // Comes with one sample + msg("[DMA%d %05d] EOB (packet len: %05d, buffer count: %05d)", from_host, prod_cnt, p16[0], input->buffer_p.count); // Comes with one sample } - + if ((p16[0] != input->buffer_p.count) && - ((p16[0] + 4) != input->buffer_p.count)) + ((p16[0] + 4) != input->buffer_p.count)) // Case of overrun packet length being padded (being rounded up) { - msg("[DMA %05d] Packet len (%d) != buffer count (%d)", prod_cnt, p16[0], input->buffer_p.count); + if (from_host == 0) + msg("[DMA%d %05d] Packet len (%05d) != buffer count (%05d), seq: %04d [0x%04x 0x%04x]", from_host, prod_cnt, p16[0], input->buffer_p.count, (p16[1] & 0x0fff), p16[0], p16[1]); } - - //msg("[DMA] 0x%04x 0x%04x 0x%04x 0x%04x", p16[0], p16[1], p16[2], p16[3]); - + + //msg("[DMA%d] 0x%04x 0x%04x 0x%04x 0x%04x", from_host, p16[0], p16[1], p16[2], p16[3]); + if (p16[1] & (((uint16_t)1) << 12)) - msg("[DMA %05d] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", prod_cnt, p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); + msg("[DMA%d %05d] 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x", from_host, prod_cnt, p32[0], p32[1], p32[2], p32[3], p32[4], p32[5]); } #endif // ENABLE_DMA_BUFFER_PACKET_DEBUG status = CyU3PDmaChannelCommitBuffer (chHandle, input->buffer_p.count, 0); @@ -594,7 +618,7 @@ void dma_callback ( LOCKP(lock); cnt->ABORTED++; UNLOCKP(lock); - + msg("! Aborted %i", from_host); } else if (type == CY_U3P_DMA_CB_ERROR) @@ -602,7 +626,7 @@ void dma_callback ( LOCKP(lock); cnt->ERROR++; UNLOCKP(lock); - + msg("! Error %i", from_host); } else if (type == CY_U3P_DMA_CB_PROD_SUSP) @@ -610,7 +634,7 @@ void dma_callback ( LOCKP(lock); cnt->PROD_SUSP++; UNLOCKP(lock); - + msg("! Prod suspend %i", from_host); } else if (type == CY_U3P_DMA_CB_CONS_SUSP) @@ -618,8 +642,10 @@ void dma_callback ( LOCKP(lock); cnt->CONS_SUSP++; UNLOCKP(lock); - - msg("! Cons suspend %i", from_host); + + //msg("! Cons suspend %i", from_host); + + CyU3PDmaChannelResume (chHandle, CyFalse, CyTrue); } } @@ -672,7 +698,7 @@ void gpio_interrupt_callback(uint8_t gpio_id) { * everything will come back up properly. */ void b200_fw_stop(void) { msg("b200_fw_stop"); - + CyU3PEpConfig_t usb_endpoint_config; /* Update the flag. */ @@ -683,14 +709,14 @@ void b200_fw_stop(void) { CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); - + /* Reset the DMA channels */ // SDK 1.3 known issue #1 - probably not necessary since Destroy is next, but just in case CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); CyU3PDmaChannelReset(&ctrl_cons_to_prod_chan_handle); CyU3PDmaChannelReset(&ctrl_prod_to_cons_chan_handle); - + /* Destroy the DMA channels */ CyU3PDmaChannelDestroy(&data_cons_to_prod_chan_handle); CyU3PDmaChannelDestroy(&data_prod_to_cons_chan_handle); @@ -716,7 +742,7 @@ void reset_gpif(void) { CyU3PGpioSetValue(GPIO_FPGA_RESET, CyTrue); // Bring down GPIF - CyU3PGpifDisable(CyTrue); + CyU3PGpifDisable(/*CyTrue*/CyFalse); /* Reset the DMA channels */ CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); @@ -725,42 +751,41 @@ void reset_gpif(void) { CyU3PDmaChannelReset(&ctrl_prod_to_cons_chan_handle); /* Reset the DMA transfers */ - CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, \ - DMA_SIZE_INFINITE); - - CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, \ - DMA_SIZE_INFINITE); - - CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, \ - DMA_SIZE_INFINITE); - - CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, \ - DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); /* Flush the USB endpoints */ CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); CyU3PUsbFlushEp(CTRL_ENDPOINT_PRODUCER); CyU3PUsbFlushEp(CTRL_ENDPOINT_CONSUMER); - + /* Load the GPIF configuration for Slave FIFO sync mode. */ - CyU3PGpifLoad(&CyFxGpifConfig); - + //CyU3PGpifLoad(&CyFxGpifConfig); + /* Start the state machine. */ - CyU3PGpifSMStart(RESET, ALPHA_RESET); - + //if (CyU3PGpifSMStart(RESET, ALPHA_RESET) != CY_U3P_SUCCESS) + // msg("! CyU3PGpifSMStart"); + /* Configure the watermarks for the slfifo-write buffers. */ - CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1); - CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1); - CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1); - CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1); - + //CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1); + //CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1); + //CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1); + //CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1); + CyU3PGpioSetValue(GPIO_FPGA_RESET, CyFalse); - + CyU3PThreadSleep(FPGA_RESET_SETTLING_TIME); - + + if (CyU3PGpifSMStart(RESET, ALPHA_RESET) != CY_U3P_SUCCESS) + msg("! CyU3PGpifSMStart"); + + msg("GPIF reset"); + b200_start_fpga_sb_gpio(); - + g_fx3_state = STATE_RUNNING; } @@ -792,11 +817,11 @@ CyU3PReturnStatus_t b200_set_io_matrix(CyBool_t fpga_config_mode) { io_config_matrix.useI2C = CyTrue; io_config_matrix.useI2S = CyFalse; io_config_matrix.useSpi = fpga_config_mode; - + res = CyU3PDeviceConfigureIOMatrix(&io_config_matrix); if (res != CY_U3P_SUCCESS) msg("! ConfigureIOMatrix"); - + return res; } @@ -814,11 +839,11 @@ CyU3PReturnStatus_t b200_gpio_init(CyBool_t set_callback) { gpio_clock_config.simpleDiv = CY_U3P_GPIO_SIMPLE_DIV_BY_2; gpio_clock_config.clkSrc = CY_U3P_SYS_CLK; gpio_clock_config.halfDiv = 0; - + res = CyU3PGpioInit(&gpio_clock_config, (set_callback ? gpio_interrupt_callback : NULL)); if (res != CY_U3P_SUCCESS) msg("! CyU3PGpioInit"); - + return res; } @@ -827,36 +852,36 @@ void sb_write(uint8_t reg, uint32_t val) { #ifdef ENABLE_FPGA_SB const int len = 32; int i; - + if (g_fpga_sb_enabled == CyFalse) return; - + reg += FPGA_SB_UART_ADDR_BASE; - + //CyU3PBusyWait(1); // Can be used after each SetValue to slow down bit changes - + // START CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // Should already be 1 CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 0); - + // ADDR[8] for (i = 7; i >= 0; i--) { uint8_t bit = ((reg & (0x1 << i)) ? 0x01 : 0x00); CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, bit); - + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // FPGA reads bit } - + // DATA[32] for (i = (len-1); i >= 0; i--) { uint8_t bit = ((val & (0x1 << i)) ? 0x01 : 0x00); CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, bit); - + CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); // FPGA reads bit } - + // STOP CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 0); CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 0); @@ -888,10 +913,10 @@ void b200_enable_fpga_sb_gpio(CyBool_t enable) { #ifdef ENABLE_FPGA_SB CyU3PGpioSimpleConfig_t gpio_config; CyU3PReturnStatus_t res; - + if (enable == CyFalse) { g_fpga_sb_enabled = CyFalse; - + return; } @@ -900,7 +925,7 @@ void b200_enable_fpga_sb_gpio(CyBool_t enable) { gpio_config.driveHighEn = CyTrue; gpio_config.inputEn = CyFalse; gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; - + res = CyU3PGpioSetSimpleConfig(GPIO_FPGA_SB_SCL, &gpio_config); if (res != CY_U3P_SUCCESS) { msg("! GpioSetSimpleConfig GPIO_FPGA_SB_SCL"); @@ -912,9 +937,9 @@ void b200_enable_fpga_sb_gpio(CyBool_t enable) { CyU3PGpioSetValue(GPIO_FPGA_SB_SCL, 1); CyU3PGpioSetValue(GPIO_FPGA_SB_SDA, 1); - + g_fpga_sb_enabled = CyTrue; - + msg("Debug SB OK"); #endif // ENABLE_FPGA_SB } @@ -923,10 +948,10 @@ void b200_enable_fpga_sb_gpio(CyBool_t enable) { void b200_start_fpga_sb_gpio(void) { #ifdef ENABLE_FPGA_SB LOCK(g_suart_lock); - sb_write(SUART_CLKDIV, g_fpga_sb_uart_div); // 16-bit reg, master clock = 100 MHz (434*2x = 230400/2) + sb_write(SUART_CLKDIV, /*g_fpga_sb_uart_div*/g_config.sb_baud_div); // 16-bit reg, master clock = 100 MHz (434*2x = 230400/2) _sb_write_string("\r\n B2x0 FPGA reset\r\n"); UNLOCK(g_suart_lock); - + msg("Compat: %d.%d", FX3_COMPAT_MAJOR, FX3_COMPAT_MINOR); msg("FX3 SDK: %d.%d.%d (build %d)", CYFX_VERSION_MAJOR, CYFX_VERSION_MINOR, CYFX_VERSION_PATCH, CYFX_VERSION_BUILD); #endif // ENABLE_FPGA_SB @@ -940,15 +965,15 @@ void b200_start_fpga_sb_gpio(void) { * application thread will re-configure some of the pins. */ void b200_gpios_pre_fpga_config(void) { CyU3PGpioSimpleConfig_t gpio_config; - + //b200_enable_fpga_sb_gpio(CyFalse); - + //CyU3PGpioDeInit(); - + b200_set_io_matrix(CyTrue); - + //b200_gpio_init(CyTrue); // This now done once during startup - + //////////////////////////////////// /* GPIO[0:32] must be set with the DeviceOverride function, instead of the @@ -989,24 +1014,24 @@ void b200_gpios_pre_fpga_config(void) { /* Initialize GPIO output values. */ CyU3PGpioSetValue(GPIO_FPGA_RESET, 0); CyU3PGpioSetValue(GPIO_PROGRAM_B, 1); - + b200_enable_fpga_sb_gpio(CyTrue); // So SCL/SDA are already high when SB state machine activates } void b200_slfifo_mode_gpio_config(void) { CyU3PGpioSimpleConfig_t gpio_config; - + //b200_enable_fpga_sb_gpio(CyFalse); - + //CyU3PGpioDeInit(); - + b200_set_io_matrix(CyFalse); //b200_gpio_init(CyFalse); // This now done once during startup - + //////////////////////////////////// - + /* GPIO[0:32] must be set with the DeviceOverride function, instead of the * SimpleEn array configuration. */ CyU3PDeviceGpioOverride(GPIO_FPGA_RESET, CyTrue); @@ -1015,7 +1040,7 @@ void b200_slfifo_mode_gpio_config(void) { CyU3PDeviceGpioOverride(GPIO_FX3_CE, CyTrue); CyU3PDeviceGpioOverride(GPIO_FX3_MISO, CyTrue); CyU3PDeviceGpioOverride(GPIO_FX3_MOSI, CyTrue); - + /* Configure GPIOs: * Outputs: * driveLowEn = True @@ -1031,13 +1056,13 @@ void b200_slfifo_mode_gpio_config(void) { gpio_config.driveHighEn = CyTrue; gpio_config.inputEn = CyFalse; gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; - + CyU3PGpioSetSimpleConfig(GPIO_FPGA_RESET, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_SHDN_SW, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_FX3_SCLK, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_FX3_CE, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_FX3_MOSI, &gpio_config); - + /* Reconfigure the GPIO configure struct for inputs that do NOT require * interrupts attached to them. */ gpio_config.outValue = CyFalse; @@ -1045,23 +1070,23 @@ void b200_slfifo_mode_gpio_config(void) { gpio_config.driveLowEn = CyFalse; gpio_config.driveHighEn = CyFalse; gpio_config.intrMode = CY_U3P_GPIO_NO_INTR; - + CyU3PGpioSetSimpleConfig(GPIO_FX3_MISO, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_AUX_PWR_ON, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_PROGRAM_B, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_INIT_B, &gpio_config); CyU3PGpioSetSimpleConfig(GPIO_DONE, &gpio_config); - + /* Initialize GPIO output values. */ CyU3PGpioSetValue(GPIO_FPGA_RESET, 0); CyU3PGpioSetValue(GPIO_SHDN_SW, 1); CyU3PGpioSetValue(GPIO_FX3_SCLK, 0); CyU3PGpioSetValue(GPIO_FX3_CE, 1); CyU3PGpioSetValue(GPIO_FX3_MOSI, 0); - + // Disabled here as only useful once FPGA has been programmed //b200_enable_fpga_sb_gpio(CyTrue); - //b200_start_fpga_sb_gpio(); // Set set up SB USART + //b200_start_fpga_sb_gpio(); // Set up SB USART } @@ -1072,7 +1097,7 @@ void b200_slfifo_mode_gpio_config(void) { * ready to receive data from the host. */ void b200_fw_start(void) { msg("b200_fw_start"); - + CyU3PDmaChannelConfig_t dma_channel_config; CyU3PEpConfig_t usb_endpoint_config; CyU3PUSBSpeed_t usb_speed; @@ -1095,9 +1120,9 @@ void b200_fw_start(void) { data_buffer_size = 512; g_vendor_req_buff_size = USB2_VREQ_BUF_SIZE; // Max 64 num_packets_per_burst = USB2_PACKETS_PER_BURST; // 1 - + data_buffer_size_to_host = data_buffer_size_from_host = data_buffer_size; - + break; case CY_U3P_SUPER_SPEED: @@ -1110,41 +1135,49 @@ void b200_fw_start(void) { msg("LPMDisable OK"); //#endif // PREVENT_LOW_POWER_MODE max_packet_size = 1024; // Per USB3 spec - + // SDK ver: total available buffer memory // 1.2.3: 204KB // 1.3.1: 188KB - + // These options should be ignored - data_buffer_count *MUST* be 1 // They follow is kept for future testing - + // 1K //data_buffer_count = 64; //data_buffer_size = 1024; - + // 4K //data_buffer_count = 8; //data_buffer_size = 4096; - + + // 8K + //data_buffer_count = 4; + //data_buffer_size = 4096*2; + // 16K //data_buffer_count = 2*2; //data_buffer_size = 16384; // Default 16K - + // 32K //data_buffer_count = 2; //data_buffer_size = 16384*2; - - data_buffer_count = 1; - data_buffer_size = ((1 << 16) - 1); - data_buffer_size -= (data_buffer_size % 1024); // Align to 1K boundary - + + //data_buffer_count = 1; + //data_buffer_size = ((1 << 16) - 1); + //data_buffer_size = 16384; + //data_buffer_size -= (data_buffer_size % 1024); // Align to 1K boundary + + data_buffer_count = g_config.dma_buffer_count; + data_buffer_size = g_config.dma_buffer_size; + data_buffer_size_to_host = data_buffer_size; data_buffer_size_from_host = data_buffer_size; - + g_vendor_req_buff_size = USB3_VREQ_BUF_SIZE; // Max 512 - num_packets_per_burst = USB3_PACKETS_PER_BURST; // 16 + num_packets_per_burst = USB3_PACKETS_PER_BURST*1+4*0; // 16 break; - + case CY_U3P_NOT_CONNECTED: msg("! CY_U3P_NOT_CONNECTED"); return; @@ -1152,9 +1185,31 @@ void b200_fw_start(void) { default: return; } - + msg("[DMA] to host: %d, from host: %d, depth: %d, burst size: %d", data_buffer_size_to_host, data_buffer_size_from_host, data_buffer_count, num_packets_per_burst); +#ifdef ENABLE_MANUAL_DMA_XFER + msg("[DMA] Callback enabled"); + +#ifdef ENABLE_P2U_SUSP_EOP + msg("[DMA] P2U_SUSP_EOP enabled"); +#endif // ENABLE_P2U_SUSP_EOP +#ifdef ENABLE_MANUAL_DMA_XFER_FROM_HOST + msg("[DMA] Manual transfers from host"); +#endif // ENABLE_MANUAL_DMA_XFER_FROM_HOST +#ifdef ENABLE_MANUAL_DMA_XFER_TO_HOST + msg("[DMA] Manual transfers to host"); +#endif // ENABLE_MANUAL_DMA_XFER_TO_HOST +#ifdef ENABLE_DMA_BUFFER_PACKET_DEBUG + msg("[DMA] Packet debugging enabled"); +#endif // ENABLE_DMA_BUFFER_PACKET_DEBUG + +#else + msg("[DMA] AUTO transfer mode"); +#endif // ENABLE_MANUAL_DMA_XFER + + msg("[DMA] Transfer override: %s", (g_config.manual_dma ? "manual" : "auto")); + /************************************************************************* * Slave FIFO Data DMA Channel Configuration *************************************************************************/ @@ -1205,6 +1260,8 @@ CY_U3P_DMA_CB_PROD_SUSP | CY_U3P_DMA_CB_CONS_SUSP | #endif // ENABLE_MANUAL_DMA_XFER 0; + //if (g_config.manual_dma == 0) + // dma_channel_config.notification = 0; // Force all off is manual is disabled dma_channel_config.cb = #if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_FROM_HOST) from_host_dma_callback; @@ -1218,16 +1275,16 @@ CY_U3P_DMA_CB_CONS_SUSP | CyU3PDmaChannelCreate (&data_cons_to_prod_chan_handle, #if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_FROM_HOST) - /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL, + (g_config.manual_dma ? /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL : CY_U3P_DMA_TYPE_AUTO), #else CY_U3P_DMA_TYPE_AUTO, #endif // ENABLE_MANUAL_DMA_XFER &dma_channel_config); - + // By default these will adopt 'usb_endpoint_config.pcktSize' //CyU3PSetEpPacketSize(DATA_ENDPOINT_PRODUCER, 16384); //CyU3PSetEpPacketSize(DATA_ENDPOINT_CONSUMER, 16384); - + /* Create a DMA AUTO channel for P2U transfer. */ dma_channel_config.size = data_buffer_size_to_host; dma_channel_config.prodSckId = DATA_RX_PPORT_SOCKET; @@ -1244,21 +1301,28 @@ CY_U3P_DMA_CB_ERROR | CY_U3P_DMA_CB_PROD_SUSP | CY_U3P_DMA_CB_CONS_SUSP | #endif // ENABLE_MANUAL_DMA_XFER +#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_P2U_SUSP_EOP) +CY_U3P_DMA_CB_CONS_SUSP | // For 'CyU3PDmaChannelSetSuspend' below +#endif // ENABLE_P2U_SUSP_EOP 0; + //if (g_config.manual_dma == 0) + // dma_channel_config.notification = 0; // Force all off is manual is disabled dma_channel_config.cb = -#if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) +#if defined(ENABLE_MANUAL_DMA_XFER) && (defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) || defined(ENABLE_P2U_SUSP_EOP)) to_host_dma_callback; #else NULL; #endif // ENABLE_MANUAL_DMA_XFER CyU3PDmaChannelCreate (&data_prod_to_cons_chan_handle, #if defined(ENABLE_MANUAL_DMA_XFER) && defined(ENABLE_MANUAL_DMA_XFER_TO_HOST) - /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL, + (g_config.manual_dma ? /*CY_U3P_DMA_TYPE_AUTO_SIGNAL*/CY_U3P_DMA_TYPE_MANUAL : CY_U3P_DMA_TYPE_AUTO), #else CY_U3P_DMA_TYPE_AUTO, #endif // ENABLE_MANUAL_DMA_XFER &dma_channel_config); - +#ifdef ENABLE_P2U_SUSP_EOP + CyU3PDmaChannelSetSuspend(&data_prod_to_cons_chan_handle, CY_U3P_DMA_SCK_SUSP_NONE, CY_U3P_DMA_SCK_SUSP_EOP); +#endif // ENABLE_P2U_SUSP_EOP /* Flush the Endpoint memory */ CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); @@ -1328,7 +1392,7 @@ CY_U3P_DMA_CB_CONS_SUSP | /* Set DMA channel transfer size. */ CyU3PDmaChannelSetXfer(&ctrl_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); CyU3PDmaChannelSetXfer(&ctrl_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); - + //CyU3PUsbEnableEPPrefetch(); // To address USB_EVENT_EP_UNDERRUN on EP 0x86 (didn't fix it though) /* Update the application status flag. */ @@ -1364,76 +1428,76 @@ void event_usb_callback (CyU3PUsbEventType_t event_type, uint16_t event_data) { b200_fw_stop(); } break; - + case CY_U3P_USB_EVENT_CONNECT: msg("USB_EVENT_CONNECT"); break; - + case CY_U3P_USB_EVENT_SUSPEND: msg("USB_EVENT_SUSPEND"); break; - + case CY_U3P_USB_EVENT_RESUME: // Known issue: this is called repeatedly after a resume //msg("USB_EVENT_RESUME"); g_counters.resume_count++; // Not locked break; - + case CY_U3P_USB_EVENT_SPEED: msg("USB_EVENT_SPEED"); break; - + case CY_U3P_USB_EVENT_SETINTF: msg("USB_EVENT_SETINTF"); break; - + case CY_U3P_USB_EVENT_SET_SEL: msg("USB_EVENT_SET_SEL"); break; - + case CY_U3P_USB_EVENT_SOF_ITP: // CyU3PUsbEnableITPEvent //msg("USB_EVENT_SOF_ITP"); break; - + case CY_U3P_USB_EVENT_EP0_STAT_CPLT: //msg("USB_EVENT_EP0_STAT_CPLT"); // Occurs each time there's a control transfer break; - + case CY_U3P_USB_EVENT_VBUS_VALID: msg("USB_EVENT_VBUS_VALID"); break; - + case CY_U3P_USB_EVENT_VBUS_REMOVED: msg("USB_EVENT_VBUS_REMOVED"); break; - + case CY_U3P_USB_EVENT_HOST_CONNECT: msg("USB_EVENT_HOST_CONNECT"); break; - + case CY_U3P_USB_EVENT_HOST_DISCONNECT: msg("USB_EVENT_HOST_DISCONNECT"); break; - + case CY_U3P_USB_EVENT_OTG_CHANGE: msg("USB_EVENT_OTG_CHANGE"); break; - + case CY_U3P_USB_EVENT_OTG_VBUS_CHG: msg("USB_EVENT_OTG_VBUS_CHG"); break; - + case CY_U3P_USB_EVENT_OTG_SRP: msg("USB_EVENT_OTG_SRP"); break; - + case CY_U3P_USB_EVENT_EP_UNDERRUN: // See SDK 1.3 known issues 17 if this happens (can probably ignore first logged occurence) LOCK(g_counters_lock); ++g_counters.usb_ep_underrun_count; UNLOCK(g_counters_lock); - + msg("! USB_EVENT_EP_UNDERRUN on EP 0x%02x", event_data); break; - + case CY_U3P_USB_EVENT_LNK_RECOVERY: msg("USB_EVENT_LNK_RECOVERY"); break; @@ -1441,16 +1505,16 @@ void event_usb_callback (CyU3PUsbEventType_t event_type, uint16_t event_data) { case CY_U3P_USB_EVENT_USB3_LNKFAIL: msg("USB_EVENT_USB3_LNKFAIL"); break; - + case CY_U3P_USB_EVENT_SS_COMP_ENTRY: msg("USB_EVENT_SS_COMP_ENTRY"); break; - + case CY_U3P_USB_EVENT_SS_COMP_EXIT: msg("USB_EVENT_SS_COMP_EXIT"); break; #endif // (CYFX_VERSION_MAJOR >= 1) && (CYFX_VERSION_MINOR >= 3) - + default: msg("! Unhandled USB event"); break; @@ -1510,7 +1574,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { if(wLength > b200_usb_product_desc[0]) { wLength = b200_usb_product_desc[0]; } - + //msg("MS string desc"); CyU3PUsbSendEP0Data(wLength, ((uint8_t *) b200_usb_product_desc)); @@ -1540,7 +1604,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { CyU3PUsbStall(wIndex, CyFalse, CyTrue); handled = CyTrue; CyU3PUsbAckSetup(); - + msg("Clear DATA_ENDPOINT_PRODUCER"); } @@ -1553,7 +1617,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { CyU3PUsbStall(wIndex, CyFalse, CyTrue); handled = CyTrue; CyU3PUsbAckSetup(); - + msg("Clear DATA_ENDPOINT_CONSUMER"); } @@ -1566,7 +1630,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { CyU3PUsbStall(wIndex, CyFalse, CyTrue); handled = CyTrue; CyU3PUsbAckSetup(); - + msg("Clear CTRL_ENDPOINT_PRODUCER"); } @@ -1579,7 +1643,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { CyU3PUsbStall(wIndex, CyFalse, CyTrue); handled = CyTrue; CyU3PUsbAckSetup(); - + msg("Clear CTRL_ENDPOINT_CONSUMER"); } } @@ -1595,7 +1659,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { switch(bRequest) { case B200_VREQ_BITSTREAM_START: { CyU3PUsbGetEP0Data(1, g_vendor_req_buffer, &read_count); - + g_fpga_programming_write_count = 0; CyU3PEventSet(&g_event_usb_config, EVENT_BITSTREAM_START, \ @@ -1614,12 +1678,12 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { } break; } - + case B200_VREQ_BITSTREAM_DATA_FILL: { CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &g_vendor_req_read_count); break; } - + case B200_VREQ_BITSTREAM_DATA_COMMIT: { /*CyU3PReturnStatus_t*/int spi_result = -1; if (g_fx3_state == STATE_CONFIGURING_FPGA) { @@ -1670,8 +1734,14 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { case B200_VREQ_GET_LOG: { LOCK(g_log_lock); - - if (log_buffer_idx == 0) + + if (log_buffer_len == 0) { + log_buffer[0] = '\0'; + CyU3PUsbSendEP0Data(1, (uint8_t*)log_buffer); + //CyU3PUsbSendEP0Data(0, NULL); + //CyU3PUsbAckSetup(); + } + else if (log_buffer_idx == 0) CyU3PUsbSendEP0Data(log_buffer_len, (uint8_t*)log_buffer); else { int len1 = min(LOG_BUFFER_SIZE - log_buffer_idx, log_buffer_len); @@ -1681,39 +1751,39 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { memcpy(log_contiguous_buffer + len1, log_buffer, log_buffer_len - len1); CyU3PUsbSendEP0Data(log_buffer_len, (uint8_t*)log_contiguous_buffer); } - + // FIXME: Necessary? Not used in the other ones //CyU3PUsbSendEP0Data(0, NULL); // Send ZLP since previous send has resulted in an integral # of packets - + log_reset(); - + UNLOCK(g_log_lock); - + //log_reset(); - + break; } case B200_VREQ_GET_COUNTERS: { LOCK(g_counters_lock); - + CyU3PUsbSendEP0Data(sizeof(COUNTERS), (uint8_t*)&g_counters); - + counters_auto_reset(); - + UNLOCK(g_counters_lock); - + //counters_auto_reset(); - + break; } - + case B200_VREQ_CLEAR_COUNTERS: { CyU3PUsbAckSetup(); //CyU3PUsbGetEP0Data(g_vendor_req_buff_size, g_vendor_req_buffer, &read_count); // Dummy - + counters_dma_reset(); - + break; } @@ -1750,14 +1820,14 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { //msg("USB event log [1] %i", (int)len); } } - + //if (len > 0) // Send a ZLP, otherwise it'll timeout CyU3PUsbSendEP0Data(len, g_usb_event_log_contiguous_buf); - + g_last_usb_event_log_index = idx; break; } - + case B200_VREQ_SET_CONFIG: { CyU3PUsbGetEP0Data(sizeof(CONFIG_MOD), (uint8_t*)g_vendor_req_buffer, &read_count); if (read_count == sizeof(CONFIG_MOD)) { @@ -1766,7 +1836,7 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { } break; } - + case B200_VREQ_GET_CONFIG: { CyU3PUsbSendEP0Data(sizeof(g_config), (uint8_t*)&g_config); break; @@ -1780,64 +1850,77 @@ CyBool_t usb_setup_callback(uint32_t data0, uint32_t data1) { for (i = 0; i < read_count; ++i) sb_write(SUART_TXCHAR, g_vendor_req_buffer[i]); UNLOCK(g_suart_lock); - + msg("Wrote %d SB chars", read_count); #else msg("SB is disabled"); #endif // ENABLE_FPGA_SB break; } - + case B200_VREQ_SET_SB_BAUD_DIV: { uint16_t div; CyU3PUsbGetEP0Data(sizeof(div), (uint8_t*)&div, &read_count); - + if (read_count == sizeof(div)) { #ifdef ENABLE_FPGA_SB LOCK(g_suart_lock); sb_write(SUART_CLKDIV, div); UNLOCK(g_suart_lock); msg("SUART_CLKDIV = %d", div); - g_fpga_sb_uart_div = div; // Store for GPIF (FPGA) reset + /*g_fpga_sb_uart_div*/g_config.sb_baud_div = div; // Store for GPIF (FPGA) reset #else msg("SB is disabled"); #endif // ENABLE_FPGA_SB } else msg("! SUART_CLKDIV received %d bytes", read_count); - + break; } - + case B200_VREQ_FLUSH_DATA_EPS: { //msg("Flushing data EPs..."); - - CyU3PUsbAckSetup(); - + + //CyU3PUsbAckSetup(); + + //CyU3PUsbResetEndpointMemories(); + // From host //CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); //CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); //CyU3PUsbResetEp(DATA_ENDPOINT_PRODUCER); //CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); - + + //CyU3PDmaSocketDisable(DATA_RX_PPORT_SOCKET); + //CyU3PDmaChannelReset(&data_cons_to_prod_chan_handle); - CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); + if (CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle) != CY_U3P_SUCCESS) + { + msg("! CyU3PDmaChannelReset"); + } //CyU3PUsbFlushEp(DATA_ENDPOINT_PRODUCER); CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); //CyU3PUsbResetEp(DATA_ENDPOINT_PRODUCER); - CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); +// CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); + //CyU3PUsbStall(DATA_ENDPOINT_CONSUMER, CyFalse, CyTrue); //CyU3PDmaChannelSetXfer(&data_cons_to_prod_chan_handle, DMA_SIZE_INFINITE); + //CyU3PDmaSocketEnable(DATA_RX_PPORT_SOCKET); CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); - + + //CyU3PUsbResetEndpointMemories(); + // To host //CyU3PDmaChannelReset(&data_prod_to_cons_chan_handle); //CyU3PUsbFlushEp(DATA_ENDPOINT_CONSUMER); //CyU3PUsbResetEp(DATA_ENDPOINT_CONSUMER); //CyU3PDmaChannelSetXfer(&data_prod_to_cons_chan_handle, DMA_SIZE_INFINITE); - + + CyU3PUsbAckSetup(); + break; } - + case B200_VREQ_EEPROM_WRITE: { i2cAddr = 0xA0 | ((wValue & 0x0007) << 1); CyU3PUsbGetEP0Data(((wLength + 15) & 0xFFF0), g_vendor_req_buffer, NULL); @@ -1941,6 +2024,97 @@ CyBool_t lpm_request_callback(CyU3PUsbLinkPowerMode link_mode) { } +/* Callback function to check for PIB ERROR*/ +void gpif_error_cb(CyU3PPibIntrType cbType, uint16_t cbArg) +{ + if (cbType==CYU3P_PIB_INTR_ERROR) + { + switch (CYU3P_GET_PIB_ERROR_TYPE(cbArg)) + { + case CYU3P_PIB_ERR_NONE: + break; + case CYU3P_PIB_ERR_THR0_WR_OVERRUN: + msg("CYU3P_PIB_ERR_THR0_WR_OVERRUN"); + break; + case CYU3P_PIB_ERR_THR1_WR_OVERRUN: + msg("CYU3P_PIB_ERR_THR1_WR_OVERRUN"); + break; + case CYU3P_PIB_ERR_THR2_WR_OVERRUN: + msg("CYU3P_PIB_ERR_THR2_WR_OVERRUN"); + break; + case CYU3P_PIB_ERR_THR3_WR_OVERRUN: + msg("CYU3P_PIB_ERR_THR3_WR_OVERRUN"); + break; + case CYU3P_PIB_ERR_THR0_RD_UNDERRUN: + msg("CYU3P_PIB_ERR_THR0_RD_UNDERRUN"); + break; + case CYU3P_PIB_ERR_THR1_RD_UNDERRUN: + msg("CYU3P_PIB_ERR_THR1_RD_UNDERRUN"); + break; + case CYU3P_PIB_ERR_THR2_RD_UNDERRUN: + msg("CYU3P_PIB_ERR_THR2_RD_UNDERRUN"); + break; + case CYU3P_PIB_ERR_THR3_RD_UNDERRUN: + msg("CYU3P_PIB_ERR_THR3_RD_UNDERRUN"); + break; + case CYU3P_PIB_ERR_THR0_ADAP_UNDERRUN: + msg("CYU3P_PIB_ERR_THR0_ADAP_UNDERRUN"); + break; + case CYU3P_PIB_ERR_THR1_ADAP_OVERRUN: + msg("CYU3P_PIB_ERR_THR1_ADAP_OVERRUN"); + break; + // FIXME: Other threads + case CYU3P_PIB_ERR_THR1_SCK_INACTIVE: + //msg("CYU3P_PIB_ERR_THR1_SCK_INACTIVE"); + ++g_counters.pib_counters[1].socket_inactive; // UNSYNC'd + break; + default: + msg("Unknown CYU3P_PIB_ERR %i", CYU3P_GET_PIB_ERROR_TYPE(cbArg)); + break; + } + + switch (CYU3P_GET_GPIF_ERROR_TYPE(cbArg)) + { + case CYU3P_GPIF_ERR_NONE: /**< No GPIF state machine errors. */ + //msg("CYU3P_GPIF_ERR_NONE"); + break; + case CYU3P_GPIF_ERR_INADDR_OVERWRITE: /**< Content of INGRESS_ADDR register is overwritten before read. */ + msg("CYU3P_GPIF_ERR_INADDR_OVERWRITE"); + break; + case CYU3P_GPIF_ERR_EGADDR_INVALID: /**< Attempt to read EGRESS_ADDR register before it is written to. */ + msg("CYU3P_GPIF_ERR_EGADDR_INVALID"); + break; + case CYU3P_GPIF_ERR_DATA_READ_ERR: /**< Read from DMA data thread which is not ready. */ + msg("CYU3P_GPIF_ERR_DATA_READ_ERR"); + break; + case CYU3P_GPIF_ERR_DATA_WRITE_ERR: /**< Write to DMA data thread which is not ready. */ + msg("CYU3P_GPIF_ERR_DATA_WRITE_ERR"); + break; + case CYU3P_GPIF_ERR_ADDR_READ_ERR: /**< Read from DMA address thread which is not ready. */ + msg("CYU3P_GPIF_ERR_ADDR_READ_ERR"); + break; + case CYU3P_GPIF_ERR_ADDR_WRITE_ERR: /**< Write to DMA address thread which is not ready. */ + msg("CYU3P_GPIF_ERR_ADDR_WRITE_ERR"); + break; + case CYU3P_GPIF_ERR_INVALID_STATE: /**< GPIF state machine has reached an invalid state. */ + //msg("CYU3P_GPIF_ERR_INVALID_STATE"); + ++g_counters.invalid_gpif_state; // UNSYNC'd + break; + default: + msg("Unknown CYU3P_GPIF_ERR %i", CYU3P_GET_GPIF_ERROR_TYPE(cbArg)); + break; + } + } +} + + +void GpifStateChangeCb(uint8_t stateId) +{ + //msg("%d", stateId); + ++g_counters.state_transition_count; +} + + /*! Initialize and start the GPIF state machine. * * This function starts the GPIF Slave FIFO state machine on the FX3. Because on @@ -1948,7 +2122,7 @@ CyBool_t lpm_request_callback(CyU3PUsbLinkPowerMode link_mode) { * after FPGA configuration is complete. */ void b200_gpif_init(void) { msg("b200_gpif_init"); - + CyU3PPibClock_t pib_clock_config; /* Initialize the p-port block; disable DLL for sync GPIF. */ @@ -1956,19 +2130,34 @@ void b200_gpif_init(void) { pib_clock_config.clkSrc = CY_U3P_SYS_CLK; pib_clock_config.isHalfDiv = CyFalse; pib_clock_config.isDllEnable = CyFalse; - CyU3PPibInit(CyTrue, &pib_clock_config); + if (CyU3PPibInit(CyTrue, &pib_clock_config) != CY_U3P_SUCCESS) + msg("! CyU3PPibInit"); /* Load the GPIF configuration for Slave FIFO sync mode. */ - CyU3PGpifLoad(&CyFxGpifConfig); + if (CyU3PGpifLoad(&CyFxGpifConfig) != CY_U3P_SUCCESS) + msg("! CyU3PGpifLoad"); + + msg("GPIF loaded"); + + //CyU3PGpifRegisterSMIntrCallback(GpifStateChangeCb); /* Start the state machine. */ - CyU3PGpifSMStart(RESET, ALPHA_RESET); + //CyU3PGpifSMStart(RESET, ALPHA_RESET); /* Configure the watermarks for the slfifo-write buffers. */ - CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1); - CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1); - CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1); - CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1); + if (CyU3PGpifSocketConfigure(0, DATA_TX_PPORT_SOCKET, 5, CyFalse, 1) != CY_U3P_SUCCESS) + msg("! CyU3PGpifSocketConfigure 0"); + if (CyU3PGpifSocketConfigure(1, DATA_RX_PPORT_SOCKET, 6, CyFalse, 1) != CY_U3P_SUCCESS) + msg("! CyU3PGpifSocketConfigure 1"); + if (CyU3PGpifSocketConfigure(2, CTRL_COMM_PPORT_SOCKET, 5, CyFalse, 1) != CY_U3P_SUCCESS) + msg("! CyU3PGpifSocketConfigure 2"); + if (CyU3PGpifSocketConfigure(3, CTRL_RESP_PPORT_SOCKET, 6, CyFalse, 1) != CY_U3P_SUCCESS) + msg("! CyU3PGpifSocketConfigure 3"); + + //CyU3PGpifSMStart(RESET, ALPHA_RESET); + + /* Register a callback for notification of PIB interrupts*/ + CyU3PPibRegisterCallback(gpif_error_cb, CYU3P_PIB_INTR_ERROR); } @@ -1979,7 +2168,7 @@ void b200_gpif_init(void) { * 32-bit mode. */ CyU3PReturnStatus_t b200_spi_init(void) { msg("b200_spi_init"); - + CyU3PSpiConfig_t spiConfig; /* Start the SPI module and configure the master. */ @@ -2000,10 +2189,10 @@ CyU3PReturnStatus_t b200_spi_init(void) { spiConfig.wordLen = 8; CyU3PReturnStatus_t res = CyU3PSpiSetConfig(&spiConfig, NULL); - + if (res != CY_U3P_SUCCESS) msg("! CyU3PSpiSetConfig"); - + return res; } @@ -2015,7 +2204,7 @@ CyU3PReturnStatus_t b200_spi_init(void) { */ void b200_usb_init(void) { //msg("b200_usb_init"); - + /* Initialize the I2C interface for the EEPROM of page size 64 bytes. */ CyFxI2cInit(CY_FX_USBI2C_I2C_PAGE_SIZE); @@ -2137,48 +2326,53 @@ void b200_usb_init(void) { CyU3PUsbSetDesc(CY_U3P_USB_SET_STRING_DESCR, 3, (uint8_t *) dev_serial); - + //////////////////////////////////////////////////////// // FIXME: CyU3PUsbSetTxDeemphasis(0x11); <0x1F // Shouldn't need to change this - uint32_t tx_swing = /*65*/45; // 65 & 45 are OK, 120 causes much link recovery. <128. 1.2V is USB3 limit. + const uint32_t tx_swing = g_config.tx_swing/*65*//*45*/; // 65 & 45 are OK, 120 causes much link recovery. <128. 1.2V is USB3 limit. if (CyU3PUsbSetTxSwing(tx_swing) == CY_U3P_SUCCESS) - msg("CyU3PUsbSetTxSwing %d", tx_swing); + msg("CyU3PUsbSetTxSwing: %d", tx_swing); else - msg("! CyU3PUsbSetTxSwing %d", tx_swing); + msg("! CyU3PUsbSetTxSwing: %d", tx_swing); //////////////////////////////////////////////////////// /* Connect the USB pins, and enable SuperSpeed (USB 3.0). */ - CyU3PConnectState(CyTrue, CyTrue); // connect, ssEnable + if (CyU3PConnectState(CyTrue, (g_config.enable_as_superspeed != 0 ? CyTrue : CyFalse)) == CY_U3P_SUCCESS) { // connect, ssEnable + CyU3PUSBSpeed_t usb_speed = CyU3PUsbGetSpeed(); + msg("Link up (speed: USB %d)", (int)usb_speed); // MAGIC: Values happen to map + } + else + msg("! Failed to establish link"); } void b200_restore_gpio_for_fpga_config(void) { CyU3PDeviceGpioRestore(GPIO_FPGA_RESET); CyU3PDeviceGpioRestore(GPIO_DONE); - + CyU3PDeviceGpioRestore(GPIO_FX3_SCLK); CyU3PDeviceGpioRestore(GPIO_FX3_CE); CyU3PDeviceGpioRestore(GPIO_FX3_MISO); CyU3PDeviceGpioRestore(GPIO_FX3_MOSI); - + //CyU3PGpioDeInit(); // Moved to just before init } void thread_fpga_config_entry(uint32_t input) { uint32_t event_flag; - + //msg("thread_fpga_config_entry"); - + for(;;) { - + // Event is set through VREQ if(CyU3PEventGet(&g_event_usb_config, \ (EVENT_FPGA_CONFIG), CYU3P_EVENT_AND_CLEAR, \ &event_flag, CYU3P_WAIT_FOREVER) == CY_U3P_SUCCESS) { - + //uint8_t old_state = g_fx3_state; uint32_t old_fpga_programming_write_count = 0; @@ -2200,7 +2394,7 @@ void thread_fpga_config_entry(uint32_t input) { /* Configure the device GPIOs for FPGA programming. */ b200_gpios_pre_fpga_config(); - + CyU3PSysWatchDogClear(); /* Initialize the SPI module that will be used for FPGA programming. */ @@ -2213,7 +2407,7 @@ void thread_fpga_config_entry(uint32_t input) { /* We can now begin configuring the FPGA. */ g_fx3_state = STATE_FPGA_READY; - + msg("Begin FPGA"); // Event is set through VREQ @@ -2232,7 +2426,7 @@ void thread_fpga_config_entry(uint32_t input) { CyU3PThreadSleep(FPGA_PROGRAMMING_POLL_SLEEP); CyU3PSysWatchDogClear(); } - + if (wait_count >= FPGA_PROGRAMMING_BITSTREAM_START_POLL_COUNT) continue; @@ -2243,7 +2437,7 @@ void thread_fpga_config_entry(uint32_t input) { /* Wait for INIT_B to fall and rise. */ wait_count = 0; - + msg("Wait FPGA"); while(CyU3PEventGet(&g_event_usb_config, \ @@ -2282,9 +2476,9 @@ void thread_fpga_config_entry(uint32_t input) { /* We are ready to accept the FPGA bitstream! */ wait_count = 0; g_fx3_state = STATE_CONFIGURING_FPGA; - + msg("Configuring FPGA"); - + // g_fpga_programming_write_count is zero'd by VREQ triggering EVENT_BITSTREAM_START while(CyU3PEventGet(&g_event_usb_config, \ @@ -2308,7 +2502,7 @@ void thread_fpga_config_entry(uint32_t input) { wait_count = 0; old_fpga_programming_write_count = g_fpga_programming_write_count; } - + CyU3PThreadSleep(FPGA_PROGRAMMING_POLL_SLEEP); CyU3PSysWatchDogClear(); } @@ -2329,42 +2523,42 @@ void thread_fpga_config_entry(uint32_t input) { #endif // ENABLE_DONE_WORKAROUND if (wait_count >= FPGA_PROGRAMMING_DONE_POLL_COUNT) continue; - + msg("FPGA done"); - + /* Tell the host that we are ignoring it for a while. */ g_fx3_state = STATE_BUSY; - + CyU3PSysWatchDogClear(); - + /* Now that the FPGA is configured, we need to tear down the current SPI and * GPIO configs, and re-config for GPIF & bit-banged SPI operation. */ CyU3PSpiDeInit(); b200_restore_gpio_for_fpga_config(); - + CyU3PSysWatchDogClear(); - + /* Load the GPIO configuration for normal SLFIFO use. */ b200_slfifo_mode_gpio_config(); - + /* Tone down the drive strength on the P-port. */ //CyU3PSetPportDriveStrength(CY_U3P_DS_HALF_STRENGTH); - + CyU3PSysWatchDogClear(); - + /* FPGA configuration is complete! Time to get the GPIF state machine * running for Slave FIFO. */ b200_gpif_init(); - + CyU3PThreadSleep(1); b200_start_fpga_sb_gpio(); // Moved here to give SB time to init - + /* RUN, BABY, RUN! */ g_fx3_state = STATE_RUNNING; - + msg("Running"); } - + CyU3PThreadRelinquish(); } } @@ -2403,15 +2597,15 @@ void thread_main_app_entry(uint32_t input) { while((current_state >= CyU3PUsbLPM_U1) \ && (current_state <= CyU3PUsbLPM_U3)) { - + msg("! LPS = %i", current_state); CyU3PUsbSetLinkPowerState(CyU3PUsbLPM_U0); // This will wake up the host if it's trying to sleep CyU3PThreadSleep(1); - + if (CyU3PUsbGetSpeed () != CY_U3P_SUPER_SPEED) break; - + CyU3PUsbGetLinkPowerState (¤t_state); } } @@ -2426,12 +2620,12 @@ static uint32_t g_poll_last_phy_error_status = 0; void update_error_counters(void) { if (CyU3PUsbGetSpeed () != CY_U3P_SUPER_SPEED) return; - + uvint32_t reg = REG_LNK_PHY_ERROR_STATUS; uint32_t val = 0; if (CyU3PReadDeviceRegisters((uvint32_t*)reg, 1, &val) == CY_U3P_SUCCESS) { g_poll_last_phy_error_status |= (val & PHYERR_MASK); - + // Reset after read uint32_t zero = PHYERR_MASK; if (CyU3PWriteDeviceRegisters((uvint32_t*)reg, 1, &zero) != CY_U3P_SUCCESS) @@ -2441,12 +2635,12 @@ void update_error_counters(void) { // FIXME: Log once msg("! Reg read fail"); } - + // Equivalent code: //uint32_t* p = (uint32_t*)REG_LNK_PHY_ERROR_STATUS; //val = (*p); //(*p) = PHYERR_MASK; - + uint16_t phy_error_count = 0, link_error_count = 0; if (CyU3PUsbGetErrorCounts(&phy_error_count, &link_error_count) == CY_U3P_SUCCESS) { // Resets internal counters after call g_poll_last_phy_error_count += phy_error_count; @@ -2456,7 +2650,7 @@ void update_error_counters(void) { // FIXME: Log once msg("! CyU3PUsbGetErrorCounts"); } - + LOCK(g_counters_lock); g_counters.usb_error_update_count++; g_counters.usb_error_counters.phy_error_count += phy_error_count; @@ -2478,50 +2672,153 @@ void update_error_counters(void) { void thread_re_enum_entry(uint32_t input) { uint32_t event_flag; - + //msg("thread_re_enum_entry"); - + int keep_alive = 0; - + while (1) { if (CyU3PEventGet(&g_event_usb_config, \ (EVENT_RE_ENUM), CYU3P_EVENT_AND_CLEAR, \ &event_flag, RE_ENUM_THREAD_SLEEP_TIME) == CY_U3P_SUCCESS) { msg("Re-config"); - + // FIXME: This section is not finished - + // Not locking this since we only expect one write in VREQ and read afterward here - - int re_enum = g_config_mod.flags & (CF_RE_ENUM | CF_TX_SWING | CF_TX_DEEMPHASIS); - + + int re_enum = g_config_mod.flags & (CF_RE_ENUM | CF_TX_SWING | CF_TX_DEEMPHASIS | CF_DISABLE_USB2 | CF_ENABLE_AS_SUPERSPEED); + CyU3PThreadSleep(100); // Wait for EP0 xaction to complete - + //b200_fw_stop(); - + +/* + int tx_swing; // [90] [65] 45 + int tx_deemphasis; // 0x11 + int disable_usb2; // 0 + int enable_as_superspeed; // 1 + int pport_drive_strength; // CY_U3P_DS_THREE_QUARTER_STRENGTH + int dma_buffer_size; // [USB3] (max) + int dma_buffer_count; // [USB3] 1 + int manual_dma; // 0 + int sb_baud_div; // 434*2 +*/ + if (re_enum) { - msg("Link down"); - CyU3PConnectState(CyFalse, CyTrue); + if (CyU3PConnectState(CyFalse, (g_config.enable_as_superspeed != 0 ? CyTrue : CyFalse)) == CY_U3P_SUCCESS) + msg("Link down"); + else + msg("! Failed to bring link down"); } - + if (g_config_mod.flags & CF_TX_DEEMPHASIS) { - //g_config_mod.config.tx_deemphasis - //CyU3PUsbSetTxDeemphasis(0x11); <0x1F +#if (CYFX_VERSION_MAJOR >= 1) && (CYFX_VERSION_MINOR >= 3) + if ((g_config_mod.config.tx_deemphasis < 0x1F) && (CyU3PUsbSetTxDeemphasis(g_config_mod.config.tx_deemphasis) == CY_U3P_SUCCESS)) { + msg("TX deemphasis now: %d (was: %d)", g_config_mod.config.tx_deemphasis, g_config.tx_deemphasis); + g_config.tx_deemphasis = g_config_mod.config.tx_deemphasis; + } + else +#endif // #if (CYFX_VERSION_MAJOR >= 1) && (CYFX_VERSION_MINOR >= 3) + msg("! Failed to set TX deemphasis: %d (still: %d)", g_config_mod.config.tx_deemphasis, g_config.tx_deemphasis); } + if (g_config_mod.flags & CF_TX_SWING) { - //CyU3PUsbSetTxSwing(90); <128 + if ((g_config_mod.config.tx_swing < 128) && (CyU3PUsbSetTxSwing(g_config_mod.config.tx_swing) == CY_U3P_SUCCESS)) { + msg("TX swing now: %d (was: %d)", g_config_mod.config.tx_swing, g_config.tx_swing); + g_config.tx_swing = g_config_mod.config.tx_swing; + } + else + msg("! Failed to set TX swing: %d (still: %d)", g_config_mod.config.tx_swing, g_config.tx_swing); + } + + if (g_config_mod.flags & CF_DISABLE_USB2) { + if (CyU3PUsbControlUsb2Support((g_config_mod.config.disable_usb2 != 0 ? CyTrue : CyFalse)) == CY_U3P_SUCCESS) { + msg("USB 2 support now: %s (was: %d)", (g_config_mod.config.disable_usb2 ? "disabled" : "enabled"), (g_config.disable_usb2 ? "disabled" : "enabled")); + g_config.disable_usb2 = g_config_mod.config.disable_usb2; + } + else + msg("! Failed to change USB 2 support to: %s (still: %s)", (g_config_mod.config.disable_usb2 ? "enabled" : "disabled"), (g_config.disable_usb2 ? "enabled" : "disabled")); + } + + if (g_config_mod.flags & CF_PPORT_DRIVE_STRENGTH) { + // CY_U3P_DS_QUARTER_STRENGTH,CY_U3P_DS_HALF_STRENGTH,CY_U3P_DS_THREE_QUARTER_STRENGTH,CY_U3P_DS_FULL_STRENGTH + if ((g_config_mod.config.pport_drive_strength >= CY_U3P_DS_QUARTER_STRENGTH) && + (g_config_mod.config.pport_drive_strength <= CY_U3P_DS_FULL_STRENGTH) && + (CyU3PSetPportDriveStrength(g_config_mod.config.pport_drive_strength) == CY_U3P_SUCCESS)) { + msg("PPort drive strength now: %d (was: %d)", g_config_mod.config.pport_drive_strength, g_config.pport_drive_strength); + g_config.pport_drive_strength = g_config_mod.config.pport_drive_strength; + } + else + msg("! Failed to set PPort drive strength: %d (still: %d)", g_config_mod.config.pport_drive_strength, g_config.pport_drive_strength); + } + + int reinit_dma = g_config_mod.flags & (CF_MANUAL_DMA | CF_DMA_BUFFER_COUNT | CF_DMA_BUFFER_SIZE); + if (re_enum) + reinit_dma = 0; // Don't need to if re-enumerating + + if (g_config_mod.flags & CF_MANUAL_DMA) { +#ifdef ENABLE_MANUAL_DMA_XFER + msg("DMA transfers will be: %s (was: %s)", (g_config_mod.config.manual_dma ? "manual" : "auto"), (g_config.manual_dma ? "manual" : "auto")); + g_config.manual_dma = g_config_mod.config.manual_dma; +#else + msg("! Manual DMA transfers not compiled into FW"); +#endif // ENABLE_MANUAL_DMA_XFER + } + + if (g_config_mod.flags & CF_DMA_BUFFER_COUNT) { + msg("DMA buffer count will be: %d (was: %d)", g_config_mod.config.dma_buffer_count, g_config.dma_buffer_count); + g_config.dma_buffer_count = g_config_mod.config.dma_buffer_count; + } + + if (g_config_mod.flags & CF_DMA_BUFFER_SIZE) { + msg("DMA buffer size will be: %d (was: %d)", g_config_mod.config.dma_buffer_size, g_config.dma_buffer_size); + g_config.dma_buffer_size = g_config_mod.config.dma_buffer_size; + } + + if (g_config_mod.flags & CF_SB_BAUD_DIV) { +#ifdef ENABLE_FPGA_SB + LOCK(g_suart_lock); + sb_write(SUART_CLKDIV, g_config_mod.config.sb_baud_div); + UNLOCK(g_suart_lock); + msg("SUART_CLKDIV now: %d (was: %d)", g_config_mod.config.sb_baud_div, g_config.sb_baud_div); + g_config.sb_baud_div = g_config_mod.config.sb_baud_div; +#else + msg("! Failed to set SUART_CLKDIV: SB is disabled (still: %d)", g_config.sb_baud_div); +#endif // ENABLE_FPGA_SB } - //CyU3PUsbControlUsb2Support(); - //b200_fw_start() + if (g_config_mod.flags & CF_ENABLE_AS_SUPERSPEED) { + msg("Enable SuperSpeed: %s (was: %s)", (g_config_mod.config.enable_as_superspeed ? "yes" : "no"), (g_config.enable_as_superspeed ? "yes" : "no")); + g_config.enable_as_superspeed = g_config_mod.config.enable_as_superspeed; + } + + if (reinit_dma) { + if (g_app_running) { + msg("Stopping FW..."); + + b200_fw_stop(); + } + + msg("Starting FW..."); + + b200_fw_start(); + } + else // Shouldn't be re-init'ing AND re-enum'ing /* Connect the USB pins, and enable SuperSpeed (USB 3.0). */ if (re_enum) { - msg("Link up"); - CyU3PConnectState(CyTrue, CyTrue); // CHECK: Assuming all other important state will persist + msg("Connecting... (as SuperSpeed: %d)", g_config.enable_as_superspeed); + + if (CyU3PConnectState(CyTrue, (g_config.enable_as_superspeed != 0 ? CyTrue : CyFalse)) == CY_U3P_SUCCESS) { // CHECK: Assuming all other important state will persist + CyU3PUSBSpeed_t usb_speed = CyU3PUsbGetSpeed(); + msg("Link up (speed: USB %d)", (int)usb_speed); // MAGIC: Values happen to map + } + else + msg("! Failed to bring link up"); } - + counters_reset_usb_errors(); } else { @@ -2533,7 +2830,7 @@ void thread_re_enum_entry(uint32_t input) { update_error_counters(); #endif // !ENABLE_FPGA_SB } - + CyU3PThreadRelinquish(); } } @@ -2548,26 +2845,26 @@ void base16_encode(uint8_t v, char out[2], char first) { #ifdef ENABLE_FPGA_SB void thread_fpga_sb_poll_entry(uint32_t input) { //msg("thread_fpga_sb_poll_entry"); - + while (1) { uint16_t i; uint8_t has_change = 0; - + update_error_counters(); - + /*if (g_poll_last_phy_error_count > 0) has_change = 1; if (g_poll_last_link_error_count > 0) has_change = 1;*/ if (g_poll_last_phy_error_status != 0) has_change = 1; - + uint16_t idx = CyU3PUsbGetEventLogIndex(); // Current *write* pointer if (idx > (USB_EVENT_LOG_SIZE-1)) { msg("! USB event log idx = %i", (int)idx); break; } - + uint8_t has_usb_events = 0; // Assuming logging won't wrap around between get calls (i.e. buffer should be long enough) if (g_fpga_sb_last_usb_event_log_index != idx) { @@ -2578,7 +2875,7 @@ void thread_fpga_sb_poll_entry(uint32_t input) { break; } } - + if (has_usb_events == 0) { for (i = 0; i < idx; i++) { if (g_usb_event_log[i] != 0x14 && g_usb_event_log[i] != 0x15 && g_usb_event_log[i] != 0x16) { // CTRL, STATUS, ACKSETUP @@ -2597,15 +2894,15 @@ void thread_fpga_sb_poll_entry(uint32_t input) { } } } - + if (has_change || has_usb_events) { LOCK(g_suart_lock); - + sb_write(SUART_TXCHAR, UPT_USB_EVENTS); - + char out[3]; out[2] = '\0'; - + if (has_usb_events) { if (idx < g_fpga_sb_last_usb_event_log_index) { for (i = g_fpga_sb_last_usb_event_log_index; i < USB_EVENT_LOG_SIZE; i++) { @@ -2614,7 +2911,7 @@ void thread_fpga_sb_poll_entry(uint32_t input) { base16_encode(g_usb_event_log[i], out, 'A'); _sb_write_string(out); } - + for (i = 0; i < idx; i++) { if (g_usb_event_log[i] == 0x14 || g_usb_event_log[i] == 0x15 || g_usb_event_log[i] == 0x16) // CTRL, STATUS, ACKSETUP continue; @@ -2631,10 +2928,10 @@ void thread_fpga_sb_poll_entry(uint32_t input) { } } } - + // USB events: A-P,A-P // PHY error status: a,a-i - + if (g_poll_last_phy_error_status != 0) { uint32_t mask; size_t offset; @@ -2645,32 +2942,32 @@ void thread_fpga_sb_poll_entry(uint32_t input) { } } } - + /*char buf[6]; - + if (g_poll_last_phy_error_count > 0) { sb_write(SUART_TXCHAR, 'b'); snprintf(buf, sizeof(buf)-1, "%d", g_poll_last_phy_error_count); _sb_write_string(buf); } - + if (g_poll_last_link_error_count > 0) { sb_write(SUART_TXCHAR, 'c'); snprintf(buf, sizeof(buf)-1, "%d", g_poll_last_link_error_count); _sb_write_string(buf); }*/ - + _sb_write_string("\r\n"); - + UNLOCK(g_suart_lock); } - + g_poll_last_phy_error_count = 0; g_poll_last_link_error_count = 0; g_poll_last_phy_error_status = 0; - + g_fpga_sb_last_usb_event_log_index = idx; - + CyU3PThreadRelinquish(); } } @@ -2692,10 +2989,10 @@ void CyFxApplicationDefine(void) { #ifdef ENABLE_FPGA_SB void *fpga_sb_poll_thread_ptr; #endif // ENABLE_FPGA_SB - + g_counters.magic = COUNTER_MAGIC; - memset(&g_config, 0xFF, sizeof(g_config)); // Initialise to -1 - + //memset(&g_config, 0xFF, sizeof(g_config)); // Initialise to -1 + CyU3PMutexCreate(&g_log_lock, CYU3P_NO_INHERIT); CyU3PMutexCreate(&g_counters_lock, CYU3P_NO_INHERIT); CyU3PMutexCreate(&g_counters_dma_from_host_lock, CYU3P_NO_INHERIT); @@ -2706,36 +3003,37 @@ void CyFxApplicationDefine(void) { #ifdef ENABLE_USB_EVENT_LOGGING CyU3PUsbInitEventLog(g_usb_event_log, USB_EVENT_LOG_SIZE); #endif // ENABLE_USB_EVENT_LOGGING - + //////////////////////////////////////////////////////// - + /* Tell the host that we are ignoring it for a while. */ g_fx3_state = STATE_BUSY; - + /* Set the FX3 compatibility number. */ compat_num[0] = FX3_COMPAT_MAJOR; compat_num[1] = FX3_COMPAT_MINOR; - + /* Initialize the USB system. */ b200_usb_init(); - + /* Turn on the Watchdog Timer. */ CyU3PSysWatchDogConfigure(CyTrue, WATCHDOG_TIMEOUT); - + /* Go do something. Probably not useful, because you aren't configured. */ g_fx3_state = STATE_UNCONFIGURED; - + //////////////////////////////////////////////////////// - + b200_gpio_init(CyTrue); - + b200_enable_fpga_sb_gpio(CyTrue); - + msg("Compat: %d.%d", FX3_COMPAT_MAJOR, FX3_COMPAT_MINOR); msg("FX3 SDK: %d.%d.%d (build %d)", CYFX_VERSION_MAJOR, CYFX_VERSION_MINOR, CYFX_VERSION_PATCH, CYFX_VERSION_BUILD); - + msg("FW built: %s %s", __TIME__, __DATE__); + //////////////////////////////////////////////////////// - + /* Create the USB event group that we will use to track USB events from the * application thread. */ CyU3PEventCreate(&g_event_usb_config); @@ -2838,14 +3136,14 @@ int main(void) { status = CyU3PDeviceCacheControl(CyTrue, CyFalse, CyFalse); // Icache, Dcache, DMAcache if (status != CY_U3P_SUCCESS) goto handle_fatal_error; - + /* Configure the IO peripherals on the FX3. The gpioSimpleEn arrays are * bitmaps, where each bit represents the GPIO of the matching index - the * second array is index + 32. */ status = b200_set_io_matrix(CyTrue); if(status != CY_U3P_SUCCESS) goto handle_fatal_error; - + /* This function calls starts the RTOS kernel. * * ABANDON ALL HOPE, YE WHO ENTER HERE */ diff --git a/firmware/fx3/b200/b200_main.h b/firmware/fx3/b200/b200_main.h index 61048abc9..9fe8b9511 100644 --- a/firmware/fx3/b200/b200_main.h +++ b/firmware/fx3/b200/b200_main.h @@ -10,7 +10,7 @@ #include "cyu3types.h" #include "cyu3usbconst.h" -#define FX3_COMPAT_MAJOR (uint8_t)(7) +#define FX3_COMPAT_MAJOR (uint8_t)(8) #define FX3_COMPAT_MINOR (uint8_t)(0) /* GPIO Pins */ diff --git a/firmware/fx3/b200/b200_usb_descriptors.c b/firmware/fx3/b200/b200_usb_descriptors.c index 03dbce74a..53979219b 100644 --- a/firmware/fx3/b200/b200_usb_descriptors.c +++ b/firmware/fx3/b200/b200_usb_descriptors.c @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // /* Define the USB 2.0 and USB 3.0 enumeration descriptions for the USRP B200 @@ -303,7 +303,7 @@ const uint8_t b200_usb_ss_config_desc[] __attribute__ ((aligned (32))) = 0x6A,0x00, /* Length of this descriptor and all sub descriptors */ 0x05, /* Number of interfaces */ 0x01, /* Configuration number */ - 0x00, /* COnfiguration string index */ + 0x00, /* Configuration string index */ 0x80, /* Config characteristics - D6: Self power; D5: Remote wakeup */ 0x01, /* Lie about the max power consumption (in 8mA unit) : 8mA */ @@ -424,6 +424,102 @@ const uint8_t b200_usb_ss_config_desc[] __attribute__ ((aligned (32))) = }; +const uint8_t b200_usb_ss_config_desc_new[] __attribute__ ((aligned (32))) = +{ + /* Configuration descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_CONFIG_DESCR, /* Configuration descriptor type */ + 0x4F,0x00, /* Length of this descriptor and all sub descriptors */ + 0x02, /* Number of interfaces */ + 0x01, /* Configuration number */ + 0x00, /* COnfiguration string index */ + 0x80, /* Config characteristics - D6: Self power; D5: Remote wakeup */ + 0x01, /* Lie about the max power consumption (in 8mA unit) : 8mA */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x00, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x00, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Interface descriptor */ + 0x09, /* Descriptor size */ + CY_U3P_USB_INTRFC_DESCR, /* Interface Descriptor type */ + 0x01, /* Interface number */ + 0x00, /* Alternate setting number */ + 0x04, /* Number of end points */ + 0xFF, /* Interface class */ + 0x00, /* Interface sub class */ + 0x00, /* Interface protocol code */ + 0x02, /* Interface descriptor string index */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Super speed endpoint companion descriptor for producer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + DATA_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for Bulk */ + + /* Super speed endpoint companion descriptor for consumer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Endpoint descriptor for producer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_PRODUCER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for bulk */ + + /* Super speed endpoint companion descriptor for producer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00, /* Service interval for the EP : 0 for bulk */ + + /* Endpoint descriptor for consumer EP */ + 0x07, /* Descriptor size */ + CY_U3P_USB_ENDPNT_DESCR, /* Endpoint descriptor type */ + CTRL_ENDPOINT_CONSUMER, /* Endpoint address and description */ + CY_U3P_USB_EP_BULK, /* Bulk endpoint type */ + 0x00,0x04, /* Max packet size = 1024 bytes */ + 0x00, /* Servicing interval for data transfers : 0 for Bulk */ + + /* Super speed endpoint companion descriptor for consumer EP */ + 0x06, /* Descriptor size */ + CY_U3P_SS_EP_COMPN_DESCR, /* SS endpoint companion descriptor type */ + (USB3_PACKETS_PER_BURST - 1), /* Max no. of packets in a burst : 0: burst 1 packet at a time */ + 0x00, /* Max streams for bulk EP = 0 (No streams) */ + 0x00,0x00 /* Service interval for the EP : 0 for bulk */ +}; + + /* Standard Language ID String Descriptor */ const uint8_t b200_string_lang_id_desc[] __attribute__ ((aligned (32))) = { diff --git a/firmware/octoclock/CMakeLists.txt b/firmware/octoclock/CMakeLists.txt index 80df0e9eb..0f2af35e9 100644 --- a/firmware/octoclock/CMakeLists.txt +++ b/firmware/octoclock/CMakeLists.txt @@ -31,7 +31,7 @@ if(NOT DEFINED PROGRAMMER) endif(NOT DEFINED PROGRAMMER) if(OCTOCLOCK_DEBUG) - message(STATUS "Debug enabled. Interrupts will be disabled.") + message(STATUS "Debug enabled.") add_definitions(-DDEBUG) endif(OCTOCLOCK_DEBUG) diff --git a/firmware/octoclock/bootloader/CMakeLists.txt b/firmware/octoclock/bootloader/CMakeLists.txt index 04bcfc492..7623a8698 100644 --- a/firmware/octoclock/bootloader/CMakeLists.txt +++ b/firmware/octoclock/bootloader/CMakeLists.txt @@ -49,7 +49,7 @@ add_custom_target( add_custom_target( upload_bootloader - ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i + ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_bootloader.hex:i WORKING_DIRECTORY ${CMAKE_BINARY_DIR} DEPENDS octoclock_bootloader_hex COMMENT "Uploading OctoClock bootloader to device with ${PROGRAMMER}" diff --git a/firmware/octoclock/bootloader/main.c b/firmware/octoclock/bootloader/main.c index 5e2e6f17e..09f740d31 100644 --- a/firmware/octoclock/bootloader/main.c +++ b/firmware/octoclock/bootloader/main.c @@ -23,13 +23,13 @@ #include <avr/eeprom.h> #include <avr/io.h> #include <avr/pgmspace.h> +#include <avr/wdt.h> +#include <avrlibdefs.h> #include <octoclock.h> #include <debug.h> #include <network.h> -#include <util/delay.h> - #include <net/enc28j60.h> #include "octoclock/common.h" @@ -40,10 +40,19 @@ */ #define TIME_PASSED (TCNT1 > (TIMER1_ONE_SECOND*5) || (TIFR & _BV(TOV1))) -//States -static bool received_cmd = false; -static bool done_burning = false; +/* + * States + */ +static bool received_cmd = false; // Received "PREPARE_FW_BURN_CMD" signal +static bool done_burning = false; // Received "FINALIZE_BURNING_CMD" signal +static bool app_checked = false; // Ran validation check on firmware +/* + * After new firmware is burned onto the device, the bootloader calculates its + * CRC and burns it into the EEPROM. When the device boots, this CRC is used + * to validate the firmware before loading it. This struct represents how the + * information is stored in the EEPROM. + */ typedef struct { uint16_t fw_len; uint16_t fw_crc; @@ -51,15 +60,22 @@ typedef struct { static crc_info_t crc_info; +/* + * What actually burns the firmware onto the device. + * + * Source: http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__boot.html + */ static void boot_program_page(uint8_t *buf, uint16_t page){ - uint16_t i; + // Disable interrupts + uint8_t sreg = SREG; + cli(); eeprom_busy_wait(); boot_page_erase(page); boot_spm_busy_wait(); // Wait until the memory is erased. - for(i = 0; i < SPM_PAGESIZE; i += 2){ + for(uint16_t i = 0; i < SPM_PAGESIZE; i += 2){ // Set up little-endian word. uint16_t w = *buf++; w += ((*buf++) << 8); @@ -68,36 +84,31 @@ static void boot_program_page(uint8_t *buf, uint16_t page){ } boot_page_write(page); // Store buffer in flash page. - boot_spm_busy_wait(); // Wait until the memory is written. + boot_spm_busy_wait(); // Wait until the memory is written. // Reenable RWW-section again. We need this if we want to jump back // to the application after bootloading. boot_rww_enable(); + + // Restore interrupt state + SREG = sreg; + sei(); } +/* + * Load firmware at given address into packet to send to host. + */ static void read_firmware(uint16_t addr, octoclock_packet_t *pkt_out){ for(size_t i = 0; i < SPM_PAGESIZE; i++){ pkt_out->data[i] = pgm_read_byte(addr+i); } } -void handle_udp_query_packet( - struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len -){ - const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload; - - //Respond to query packets - if(pkt_in->code == OCTOCLOCK_QUERY_CMD){ - octoclock_packet_t pkt_out; - pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER; - pkt_out.sequence = pkt_in->sequence; - pkt_out.code = OCTOCLOCK_QUERY_ACK; - pkt_out.len = 0; - send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t)); - } -} - +/* + * Calculate the CRC of the current firmware. + * + * Adapted from _crc16_update in <util/crc16.h>. + */ static void calculate_crc(uint16_t *crc, uint16_t len){ *crc = 0xFFFF; @@ -110,6 +121,11 @@ static void calculate_crc(uint16_t *crc, uint16_t len){ } } +/* + * Calculate the CRC of the current firmware. If it matches the + * CRC burned into the EEPROM, the firmware is considered valid, + * and the bootloader can load it. + */ static bool valid_app(){ crc_info_t crc_eeprom_info; eeprom_read_block(&crc_eeprom_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4); @@ -118,6 +134,26 @@ static bool valid_app(){ return (crc_info.fw_crc == crc_eeprom_info.fw_crc); } +/* + * UDP handlers + */ +void handle_udp_query_packet( + struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len +){ + const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload; + + // Respond to uhd::device::find(), identify as bootloader + if(pkt_in->code == OCTOCLOCK_QUERY_CMD){ + octoclock_packet_t pkt_out; + pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER; + pkt_out.sequence = pkt_in->sequence; + pkt_out.code = OCTOCLOCK_QUERY_ACK; + pkt_out.len = 0; + send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t)); + } +} + void handle_udp_fw_packet( struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len @@ -132,24 +168,27 @@ void handle_udp_fw_packet( case PREPARE_FW_BURN_CMD: received_cmd = true; done_burning = false; + crc_info.fw_crc = pkt_in->crc; crc_info.fw_len = pkt_in->len; pkt_out.code = FW_BURN_READY_ACK; break; + // Burn firmware sent from the host case FILE_TRANSFER_CMD: boot_program_page(pkt_in->data, pkt_in->addr); pkt_out.code = FILE_TRANSFER_ACK; + pkt_out.addr = pkt_in->addr; break; + // Send firmware back to the host for verification case READ_FW_CMD: pkt_out.code = READ_FW_ACK; read_firmware(pkt_in->addr, &pkt_out); break; + // Calculate the CRC of the new firmware and finish case FINALIZE_BURNING_CMD: - //With stuff verified, burn CRC info into EEPROM done_burning = true; - calculate_crc(&(crc_info.fw_crc), crc_info.fw_len); eeprom_write_block(&crc_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4); pkt_out.code = FINALIZE_BURNING_ACK; break; @@ -170,11 +209,12 @@ void handle_udp_eeprom_packet( pkt_out.sequence = pkt_in->sequence; pkt_out.len = 0; + // Restore OctoClock's EEPROM to factory state if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM){ switch(pkt_in->code){ case CLEAR_EEPROM_CMD: received_cmd = true; - uint8_t blank_eeprom[103]; + uint8_t blank_eeprom[103]; // 103 is offset of CRC info memset(blank_eeprom, 0xFF, 103); eeprom_write_block(blank_eeprom, 0, 103); pkt_out.code = CLEAR_EEPROM_ACK; @@ -189,28 +229,45 @@ void handle_udp_eeprom_packet( int main(void){ - asm("cli"); + // Disable watchdog timer + wdt_disable(); + + // Give interrupts to bootloader + MCUCR = (1<<IVCE); + MCUCR = (1<<IVSEL); + cli(); - //Initialization + // Atmega128 setup_atmel_io_ports(); + + // Start timer + TIMER1_INIT(); + + // Ethernet stack network_init(); - init_udp_listeners(); register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_query_packet); register_udp_listener(OCTOCLOCK_UDP_FW_PORT, handle_udp_fw_packet); register_udp_listener(OCTOCLOCK_UDP_EEPROM_PORT, handle_udp_eeprom_packet); - //Turn LED's on to show we're in the bootloader + // Turn LED's on to show we're in the bootloader PORTC |= 0x20; PORTC |= (0x20<<1); PORTC |= (0x20<<2); - TIMER1_INIT(); - bool app_checked = false; - + /* + * This loop determines whether the OctoClock will remain in its bootloader + * state or if it will load the main firmware. After five seconds, it will + * check to see if valid firmware is installed. If so, it will immediately + * load it. Otherwise, it will remain here until firmware is installed. + * + * This process can be stopped by an instruction from the firmware burner + * utility, at which point the OctoClock will remain in bootloader state until + * instructed by the utility to exit the loop and load the new firmware. + */ while(true){ if(done_burning){ if(valid_app()) break; - else done_burning = false; //Burning somehow failed and wasn't caught + else done_burning = false; // Burning somehow failed and wasn't caught } if(!app_checked && !received_cmd && TIME_PASSED){ app_checked = true; @@ -220,16 +277,18 @@ int main(void){ network_check(); } - //Turn LED's off before moving to application + // Turn LED's off before moving to application PORTC &= ~0x20; PORTC &= ~(0x20<<1); PORTC &= ~(0x20<<2); /* - * Whether the bootloader reaches here through five seconds of inactivity - * or after a firmware burn just finished, it can be assumed that the application - * is valid. + * At this point, the bootloader has determined that there is valid + * firmware installed on the device and that it is OK to load it. */ + TIMER1_DISABLE(); + MCUCR = (1<<IVCE); + MCUCR = 0; + cli(); asm("jmp 0000"); - return 0; //Will never get here, but AVR-GCC needs it } diff --git a/firmware/octoclock/include/clkdist.h b/firmware/octoclock/include/clkdist.h index 633df9ddf..357daf37b 100644 --- a/firmware/octoclock/include/clkdist.h +++ b/firmware/octoclock/include/clkdist.h @@ -23,7 +23,7 @@ #include <octoclock.h> typedef enum { - Reg0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, + Reg0=0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, Reg8_Status_Control, Read_Command=0xE, RAM_EEPROM_Unlock=0x1F, @@ -46,6 +46,8 @@ void reset_TI_CDCE18005(void); uint32_t get_TI_CDCE18005(CDCE18005 which_register); +void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits); + bool check_TI_CDCE18005(TI_Input_10_MHz which_input, CDCE18005 which_register); #endif /* _CLKDIST_H_ */ diff --git a/firmware/octoclock/include/debug.h b/firmware/octoclock/include/debug.h index ee0618bc6..1f69896f0 100644 --- a/firmware/octoclock/include/debug.h +++ b/firmware/octoclock/include/debug.h @@ -66,8 +66,8 @@ DEBUG_LOG_HEX(((uint8_t*)&num)[0]); #define DEBUG_LOG_INT(num) DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[3]); \ - DEBUG_LOG_HEX(((uint8_t*)&num)[2]); \ - DEBUG_LOG_HEX(((uint8_t*)&num)[1]); \ + DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[2]); \ + DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[1]); \ DEBUG_LOG_HEX(((uint8_t*)&num)[0]); #else diff --git a/firmware/octoclock/include/gpsdo.h b/firmware/octoclock/include/gpsdo.h index fc7d87324..df0440404 100644 --- a/firmware/octoclock/include/gpsdo.h +++ b/firmware/octoclock/include/gpsdo.h @@ -27,6 +27,4 @@ gpsdo_cache_state_t gpsdo_state; void send_gpsdo_cmd(char* buf, uint8_t size); -void process_gpsdo_output(void); - #endif /* _GPSDO_H_ */ diff --git a/firmware/octoclock/include/net/enc28j60.h b/firmware/octoclock/include/net/enc28j60.h index 463303f3c..dc58b451b 100644 --- a/firmware/octoclock/include/net/enc28j60.h +++ b/firmware/octoclock/include/net/enc28j60.h @@ -1,299 +1,276 @@ -/*! \file enc28j60.h \brief Microchip ENC28J60 Ethernet Interface Driver. */ -//***************************************************************************** -// -// File Name : 'enc28j60.h' -// Title : Microchip ENC28J60 Ethernet Interface Driver -// Author : Pascal Stang (c)2005 -// Created : 9/22/2005 -// Revised : 9/22/2005 -// Version : 0.1 -// Target MCU : Atmel AVR series -// Editor Tabs : 4 -// -/// \ingroup network -/// \defgroup enc28j60 Microchip ENC28J60 Ethernet Interface Driver (enc28j60.c) -/// \code #include "net/enc28j60.h" \endcode -/// \par Overview -/// This driver provides initialization and transmit/receive -/// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -/// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -/// chip, using an SPI interface to the host processor. -/// -// -//***************************************************************************** -//@{ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ -#ifndef ENC28J60_H -#define ENC28J60_H +#ifndef _NET_ENC28J60_H_ +#define _NET_ENC28J60_H_ -#include <avrlibdefs.h> +#include <avr/io.h> -#include "enc28j60conf.h" +#define SPI_DDR DDRB +#define SPI_PORT PORTB +#define SPI_CS 0 +#define SPI_MOSI 2 +#define SPI_MISO 3 +#define SPI_SCK 1 -// ENC28J60 Control Registers -// Control register definitions are a combination of address, -// bank number, and Ethernet/MAC/PHY indicator bits. -// - Register address (bits 0-4) -// - Bank number (bits 5-6) -// - MAC/PHY indicator (bit 7) -#define ADDR_MASK 0x1F -#define BANK_MASK 0x60 -#define SPRD_MASK 0x80 -// All-bank registers -#define EIE 0x1B -#define EIR 0x1C -#define ESTAT 0x1D -#define ECON2 0x1E -#define ECON1 0x1F -// Bank 0 registers -#define ERDPTL (0x00|0x00) -#define ERDPTH (0x01|0x00) -#define EWRPTL (0x02|0x00) -#define EWRPTH (0x03|0x00) -#define ETXSTL (0x04|0x00) -#define ETXSTH (0x05|0x00) -#define ETXNDL (0x06|0x00) -#define ETXNDH (0x07|0x00) -#define ERXSTL (0x08|0x00) -#define ERXSTH (0x09|0x00) -#define ERXNDL (0x0A|0x00) -#define ERXNDH (0x0B|0x00) -#define ERXRDPTL (0x0C|0x00) -#define ERXRDPTH (0x0D|0x00) -#define ERXWRPTL (0x0E|0x00) -#define ERXWRPTH (0x0F|0x00) -#define EDMASTL (0x10|0x00) -#define EDMASTH (0x11|0x00) -#define EDMANDL (0x12|0x00) -#define EDMANDH (0x13|0x00) -#define EDMADSTL (0x14|0x00) -#define EDMADSTH (0x15|0x00) -#define EDMACSL (0x16|0x00) -#define EDMACSH (0x17|0x00) -// Bank 1 registers -#define EHT0 (0x00|0x20) -#define EHT1 (0x01|0x20) -#define EHT2 (0x02|0x20) -#define EHT3 (0x03|0x20) -#define EHT4 (0x04|0x20) -#define EHT5 (0x05|0x20) -#define EHT6 (0x06|0x20) -#define EHT7 (0x07|0x20) -#define EPMM0 (0x08|0x20) -#define EPMM1 (0x09|0x20) -#define EPMM2 (0x0A|0x20) -#define EPMM3 (0x0B|0x20) -#define EPMM4 (0x0C|0x20) -#define EPMM5 (0x0D|0x20) -#define EPMM6 (0x0E|0x20) -#define EPMM7 (0x0F|0x20) -#define EPMCSL (0x10|0x20) -#define EPMCSH (0x11|0x20) -#define EPMOL (0x14|0x20) -#define EPMOH (0x15|0x20) -#define EWOLIE (0x16|0x20) -#define EWOLIR (0x17|0x20) -#define ERXFCON (0x18|0x20) -#define EPKTCNT (0x19|0x20) -// Bank 2 registers -#define MACON1 (0x00|0x40|0x80) -#define MACON2 (0x01|0x40|0x80) -#define MACON3 (0x02|0x40|0x80) -#define MACON4 (0x03|0x40|0x80) -#define MABBIPG (0x04|0x40|0x80) -#define MAIPGL (0x06|0x40|0x80) -#define MAIPGH (0x07|0x40|0x80) -#define MACLCON1 (0x08|0x40|0x80) -#define MACLCON2 (0x09|0x40|0x80) -#define MAMXFLL (0x0A|0x40|0x80) -#define MAMXFLH (0x0B|0x40|0x80) -#define MAPHSUP (0x0D|0x40|0x80) -#define MICON (0x11|0x40|0x80) -#define MICMD (0x12|0x40|0x80) -#define MIREGADR (0x14|0x40|0x80) -#define MIWRL (0x16|0x40|0x80) -#define MIWRH (0x17|0x40|0x80) -#define MIRDL (0x18|0x40|0x80) -#define MIRDH (0x19|0x40|0x80) -// Bank 3 registers -#define MAADR1 (0x00|0x60|0x80) -#define MAADR0 (0x01|0x60|0x80) -#define MAADR3 (0x02|0x60|0x80) -#define MAADR2 (0x03|0x60|0x80) -#define MAADR5 (0x04|0x60|0x80) -#define MAADR4 (0x05|0x60|0x80) -#define EBSTSD (0x06|0x60) -#define EBSTCON (0x07|0x60) -#define EBSTCSL (0x08|0x60) -#define EBSTCSH (0x09|0x60) -#define MISTAT (0x0A|0x60|0x80) -#define EREVID (0x12|0x60) -#define ECOCON (0x15|0x60) -#define EFLOCON (0x17|0x60) -#define EPAUSL (0x18|0x60) -#define EPAUSH (0x19|0x60) -// PHY registers -#define PHCON1 0x00 -#define PHSTAT1 0x01 -#define PHHID1 0x02 -#define PHHID2 0x03 -#define PHCON2 0x10 -#define PHSTAT2 0x11 -#define PHIE 0x12 -#define PHIR 0x13 -#define PHLCON 0x14 +// Register Masks +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define SPRD_MASK 0x80 -// ENC28J60 EIE Register Bit Definitions -#define EIE_INTIE 0x80 -#define EIE_PKTIE 0x40 -#define EIE_DMAIE 0x20 -#define EIE_LINKIE 0x10 -#define EIE_TXIE 0x08 -#define EIE_WOLIE 0x04 -#define EIE_TXERIE 0x02 -#define EIE_RXERIE 0x01 -// ENC28J60 EIR Register Bit Definitions -#define EIR_PKTIF 0x40 -#define EIR_DMAIF 0x20 -#define EIR_LINKIF 0x10 -#define EIR_TXIF 0x08 -#define EIR_WOLIF 0x04 -#define EIR_TXERIF 0x02 -#define EIR_RXERIF 0x01 -// ENC28J60 ESTAT Register Bit Definitions -#define ESTAT_INT 0x80 -#define ESTAT_LATECOL 0x10 -#define ESTAT_RXBUSY 0x04 -#define ESTAT_TXABRT 0x02 -#define ESTAT_CLKRDY 0x01 -// ENC28J60 ECON2 Register Bit Definitions -#define ECON2_AUTOINC 0x80 -#define ECON2_PKTDEC 0x40 -#define ECON2_PWRSV 0x20 -#define ECON2_VRPS 0x08 -// ENC28J60 ECON1 Register Bit Definitions -#define ECON1_TXRST 0x80 -#define ECON1_RXRST 0x40 -#define ECON1_DMAST 0x20 -#define ECON1_CSUMEN 0x10 -#define ECON1_TXRTS 0x08 -#define ECON1_RXEN 0x04 -#define ECON1_BSEL1 0x02 -#define ECON1_BSEL0 0x01 -// ENC28J60 MACON1 Register Bit Definitions -#define MACON1_LOOPBK 0x10 -#define MACON1_TXPAUS 0x08 -#define MACON1_RXPAUS 0x04 -#define MACON1_PASSALL 0x02 -#define MACON1_MARXEN 0x01 -// ENC28J60 MACON2 Register Bit Definitions -#define MACON2_MARST 0x80 -#define MACON2_RNDRST 0x40 -#define MACON2_MARXRST 0x08 -#define MACON2_RFUNRST 0x04 -#define MACON2_MATXRST 0x02 -#define MACON2_TFUNRST 0x01 -// ENC28J60 MACON3 Register Bit Definitions -#define MACON3_PADCFG2 0x80 -#define MACON3_PADCFG1 0x40 -#define MACON3_PADCFG0 0x20 -#define MACON3_TXCRCEN 0x10 -#define MACON3_PHDRLEN 0x08 -#define MACON3_HFRMLEN 0x04 -#define MACON3_FRMLNEN 0x02 -#define MACON3_FULDPX 0x01 -// ENC28J60 MICMD Register Bit Definitions -#define MICMD_MIISCAN 0x02 -#define MICMD_MIIRD 0x01 -// ENC28J60 MISTAT Register Bit Definitions -#define MISTAT_NVALID 0x04 -#define MISTAT_SCAN 0x02 -#define MISTAT_BUSY 0x01 -// ENC28J60 PHY PHCON1 Register Bit Definitions -#define PHCON1_PRST 0x8000 -#define PHCON1_PLOOPBK 0x4000 -#define PHCON1_PPWRSV 0x0800 -#define PHCON1_PDPXMD 0x0100 -// ENC28J60 PHY PHSTAT1 Register Bit Definitions -#define PHSTAT1_PFDPX 0x1000 -#define PHSTAT1_PHDPX 0x0800 -#define PHSTAT1_LLSTAT 0x0004 -#define PHSTAT1_JBSTAT 0x0002 -// ENC28J60 PHY PHCON2 Register Bit Definitions -#define PHCON2_FRCLINK 0x4000 -#define PHCON2_TXDIS 0x2000 -#define PHCON2_JABBER 0x0400 -#define PHCON2_HDLDIS 0x0100 +// All Banks Registers +#define EIE 0x1B +#define EIR 0x1C +#define ESTAT 0x1D +#define ECON2 0x1E +#define ECON1 0x1F -// ENC28J60 Packet Control Byte Bit Definitions -#define PKTCTRL_PHUGEEN 0x08 -#define PKTCTRL_PPADEN 0x04 -#define PKTCTRL_PCRCEN 0x02 -#define PKTCTRL_POVERRIDE 0x01 +// Bank 0 Registers +#define ERDPTL 0x00 +#define ERDPTH 0x01 +#define EWRPTL 0x02 +#define EWRPTH 0x03 +#define ETXSTL 0x04 +#define ETXSTH 0x05 +#define ETXNDL 0x06 +#define ETXNDH 0x07 +#define ERXSTL 0x08 +#define ERXSTH 0x09 +#define ERXNDL 0x0A +#define ERXNDH 0x0B +#define ERXRDPTL 0x0C +#define ERXRDPTH 0x0D +#define ERXWRPTL 0x0E +#define ERXWRPTH 0x0F +#define EDMASTL 0x10 +#define EDMASTH 0x11 +#define EDMANDL 0x12 +#define EDMANDH 0x13 +#define EDMADSTL 0x14 +#define EDMADSTH 0x15 +#define EDMACSL 0x16 +#define EDMACSH 0x17 -// SPI operation codes -#define ENC28J60_READ_CTRL_REG 0x00 -#define ENC28J60_READ_BUF_MEM 0x3A -#define ENC28J60_WRITE_CTRL_REG 0x40 -#define ENC28J60_WRITE_BUF_MEM 0x7A -#define ENC28J60_BIT_FIELD_SET 0x80 -#define ENC28J60_BIT_FIELD_CLR 0xA0 -#define ENC28J60_SOFT_RESET 0xFF +// Bank 1 Registers +#define EHT0 0x20 +#define EHT1 0x21 +#define EHT2 0x22 +#define EHT3 0x23 +#define EHT4 0x24 +#define EHT5 0x25 +#define EHT6 0x26 +#define EHT7 0x27 +#define EPMM0 0x28 +#define EPMM1 0x29 +#define EPMM2 0x2A +#define EPMM3 0x2B +#define EPMM4 0x2C +#define EPMM5 0x2D +#define EPMM6 0x2E +#define EPMM7 0x2F +#define EPMCSL 0x30 +#define EPMCSH 0x31 +#define EPMOL 0x34 +#define EPMOH 0x35 +#define EWOLIE 0x36 +#define EWOLIR 0x37 +#define ERXFCON 0x38 +#define EPKTCNT 0x39 +// Bank 2 Register +#define MACON1 0xC0 +#define MACON2 0xC1 +#define MACON3 0xC2 +#define MACON4 0xC3 +#define MABBIPG 0xC4 +#define MAIPGL 0xC6 +#define MAIPGH 0xC7 +#define MACLCON1 0xC8 +#define MACLCON2 0xC9 +#define MAMXFLL 0xCA +#define MAMXFLH 0xCB +#define MAPHSUP 0xCD +#define MICON 0xD1 +#define MICMD 0xD2 +#define MIREGADR 0xD4 +#define MIWRL 0xD6 +#define MIWRH 0xD7 +#define MIRDL 0xD8 +#define MIRDH 0xD9 -// buffer boundaries applied to internal 8K ram -// entire available packet buffer space is allocated -#define TXSTART_INIT 0x0000 // start TX buffer at 0 -#define RXSTART_INIT 0x0600 // give TX buffer space for one full ethernet frame (~1500 bytes) -#define RXSTOP_INIT 0x1FFF // receive buffer gets the rest +// Bank 3 Registers +#define MAADR1 0xE0 +#define MAADR0 0xE1 +#define MAADR3 0xE2 +#define MAADR2 0xE3 +#define MAADR5 0xE4 +#define MAADR4 0xE5 +#define EBSTSD 0x66 +#define EBSTCON 0x67 +#define EBSTCSL 0x68 +#define EBSTCSH 0x69 +#define MISTAT 0xEA +#define EREVID 0x72 +#define ECOCON 0x75 +#define EFLOCON 0x77 +#define EPAUSL 0x78 +#define EPAUSH 0x79 -#define MAX_FRAMELEN 1518 // maximum ethernet frame length +// PHY Registers +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHHID1 0x02 +#define PHHID2 0x03 +#define PHCON2 0x10 +#define PHSTAT2 0x11 +#define PHIE 0x12 +#define PHIR 0x13 +#define PHLCON 0x14 -// Ethernet constants -#define ETHERNET_MIN_PACKET_LENGTH 0x3C -//#define ETHERNET_HEADER_LENGTH 0x0E +// ERXFCON bit definitions +#define UCEN 0x80 +#define ANDOR 0x40 +#define CRCEN 0x20 +#define PMEN 0x10 +#define MPEN 0x08 +#define HTEN 0x04 +#define MCEN 0x02 +#define BCEN 0x01 -// setup ports for I/O -//void ax88796SetupPorts(void); +// EIE bit definitions +#define INTIE 0x80 +#define PKTIE 0x40 +#define DMAIE 0x20 +#define LINKIE 0x10 +#define TXIE 0x08 +#define WOLIE 0x04 +#define TXERIE 0x02 +#define RXERIE 0x01 -//! do a ENC28J60 read operation -u08 enc28j60ReadOp(u08 op, u08 address); -//! do a ENC28J60 write operation -void enc28j60WriteOp(u08 op, u08 address, u08 data); -//! read the packet buffer memory -void enc28j60ReadBuffer(u16 len, u08* data); -//! write the packet buffer memory -void enc28j60WriteBuffer(u16 len, u08* data); -//! set the register bank for register at address -void enc28j60SetBank(u08 address); -//! read ax88796 register -u08 enc28j60Read(u08 address); -//! write ax88796 register -void enc28j60Write(u08 address, u08 data); -//! read a PHY register -u16 enc28j60PhyRead(u08 address); -//! write a PHY register -void enc28j60PhyWrite(u08 address, u16 data); +// EIR bit definitions +#define PKTIF 0x40 +#define DMAIF 0x20 +#define LINKIF 0x10 +#define TXIF 0x08 +#define WOLIF 0x04 +#define TXERIF 0x02 +#define RXERIF 0x01 -//! initialize the ethernet interface for transmit/receive -void enc28j60Init(u08* macaddr); +// ESTAT bit definitions +#define INT 0x80 +#define LATECOL 0x10 +#define RXBUSY 0x04 +#define TXABRT 0x02 +#define CLKRDY 0x01 -//! Packet transmit function. -/// Sends a packet on the network. It is assumed that the packet is headed by a valid ethernet header. -/// \param len Length of packet in bytes. -/// \param packet Pointer to packet data. -/// \param len2 Length of the secound packet in bytes, can be 0. -/// \param packet2 Pointer to the secound packet data, can be NULL. -void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2); +// ECON2 bit definitions +#define AUTOINC 0x80 +#define PKTDEC 0x40 +#define PWRSV 0x20 +#define VRPS 0x08 -//! Packet receive function. -/// Gets a packet from the network receive buffer, if one is available. -/// The packet will by headed by an ethernet header. -/// \param maxlen The maximum acceptable length of a retrieved packet. -/// \param buf Pointer to buffer. -/// \return Packet length in bytes if a packet was retrieved, zero otherwise. -unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf); +// ECON1 bit definitions +#define TXRST 0x80 +#define RXRST 0x40 +#define DMAST 0x20 +#define CSUMEN 0x10 +#define TXRTS 0x08 +#define ENCRXEN 0x04 +#define BSEL1 0x02 +#define BSEL0 0x01 -#endif -//@} +// MACON1 bit definitions +#define LOOPBK 0x10 +#define TXPAUS 0x08 +#define RXPAUS 0x04 +#define PASSALL 0x02 +#define MARXEN 0x01 +// MACON2 bit definitions +#define MARST 0x80 +#define RNDRST 0x40 +#define MARXRST 0x08 +#define RFUNRST 0x04 +#define MATXRST 0x02 +#define TFUNRST 0x01 + +// MACON3 bit definitions +#define PADCFG2 0x80 +#define PADCFG1 0x40 +#define PADCFG0 0x20 +#define TXCRCEN 0x10 +#define PHDRLEN 0x08 +#define HFRMLEN 0x04 +#define FRMLNEN 0x02 +#define FULDPX 0x01 + +// MICMD bit definitions +#define MIISCAN 0x02 +#define MIIRD 0x01 + +// MISTAT bit definitions +#define NVALID 0x04 +#define SCAN 0x02 +#define BUSY 0x01 + +// PHCON1 bit definitions +#define PRST 0x8000 +#define PLOOPBK 0x4000 +#define PPWRSV 0x0800 +#define PDPXMD 0x0100 + +// PHSTAT1 bit definitions +#define PFDPX 0x1000 +#define PHDPX 0x0800 +#define LLSTAT 0x0004 +#define JBSTAT 0x0002 + +// PHCON2 bit definitions +#define FRCLINK 0x4000 +#define TXDIS 0x2000 +#define JABBER 0x0400 +#define HDLDIS 0x0100 + +// Packet Control bit Definitions +#define PHUGEEN 0x08 +#define PPADEN 0x04 +#define PCRCEN 0x02 +#define POVERRIDE 0x01 + +// SPI Instruction Set +#define RCR 0x00 // Read Control Register +#define RBM 0x3A // Read Buffer Memory +#define WCR 0x40 // Write Control Register +#define WBM 0x7A // Write Buffer Memory +#define BFS 0x80 // Bit Field Set +#define BFC 0xA0 // Bit Field Clear +#define SC 0xFF // Soft Reset + +// Buffer +#define RXSTART_INIT 0x0000 +#define RXSTOP_INIT (0x1FFF-0x0600-1) +#define TXSTART_INIT (0x1FFF-0x0600) +#define TXSTOP_INIT 0x1FFF +#define MAX_FRAMELEN 1500 + +void enc28j60_init(uint8_t* mac_addr); + +uint16_t enc28j60_recv(uint8_t* buffer, uint16_t max_len); + +void enc28j60_send(uint8_t* buffer, uint16_t len); + +#endif /* _NET_ENC28J60_H_ */ diff --git a/firmware/octoclock/include/net/enc28j60conf.h b/firmware/octoclock/include/net/enc28j60conf.h deleted file mode 100644 index 0acf5473c..000000000 --- a/firmware/octoclock/include/net/enc28j60conf.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! \file enc28j60conf.h \brief Microchip ENC28J60 Ethernet Interface Driver Configuration. */ -//***************************************************************************** -// -// File Name : 'enc28j60conf.h' -// Title : Microchip ENC28J60 Ethernet Interface Driver Configuration -// Author : Pascal Stang -// Created : 10/5/2004 -// Revised : 8/22/2005 -// Version : 0.1 -// Target MCU : Atmel AVR series -// Editor Tabs : 4 -// -// Description : This driver provides initialization and transmit/receive -// functions for the ENC28J60 10Mb Ethernet Controller and PHY. -// -// This code is distributed under the GNU Public License -// which can be found at http://www.gnu.org/licenses/gpl.txt -// -//***************************************************************************** - -#ifndef ENC28J60CONF_H -#define ENC28J60CONF_H - -#include <stdint.h> -typedef uint8_t u08; -typedef uint16_t u16; -typedef uint32_t u32; - -// ENC28J60 SPI port -#define ENC28J60_SPI_PORT PORTB -#define ENC28J60_SPI_DDR DDRB -#define ENC28J60_SPI_SCK PORTB1 -#define ENC28J60_SPI_MOSI PORTB2 -#define ENC28J60_SPI_MISO PORTB3 -#define ENC28J60_SPI_SS PORTB0 -// ENC28J60 control port -#define ENC28J60_CONTROL_PORT PORTB -#define ENC28J60_CONTROL_DDR DDRB -#define ENC28J60_CONTROL_CS PORTB0 - -// MAC address for this interface -#define ENC28J60_MAC0 '0' -#define ENC28J60_MAC1 'F' -#define ENC28J60_MAC2 'F' -#define ENC28J60_MAC3 'I' -#define ENC28J60_MAC4 'C' -#define ENC28J60_MAC5 'E' - -#endif /* ENC28J60CONF_H */ diff --git a/firmware/octoclock/include/net/eth_mac_addr.h b/firmware/octoclock/include/net/eth_mac_addr.h index 0c790aa4f..78986bf04 100644 --- a/firmware/octoclock/include/net/eth_mac_addr.h +++ b/firmware/octoclock/include/net/eth_mac_addr.h @@ -21,11 +21,8 @@ #include <stdint.h> // Ethernet MAC address - -#pragma pack(push,1) typedef struct { uint8_t addr[6]; -} eth_mac_addr_t; -#pragma pack(pop) +} eth_mac_addr_t __attribute__((aligned(1))); #endif /* INCLUDED_ETH_MAC_ADDR_H */ diff --git a/firmware/octoclock/include/network.h b/firmware/octoclock/include/network.h index 83e398bc5..6d126a197 100644 --- a/firmware/octoclock/include/network.h +++ b/firmware/octoclock/include/network.h @@ -69,17 +69,11 @@ #define _IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (_IPH_TTL(hdr) << 8))) #define _IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) -bool using_network_defaults; +volatile bool using_network_defaults; // Ethernet I/O buffers -uint8_t buf_in[512]; -uint8_t buf_out[512]; - -// Default values loaded if EEPROM is incomplete -static const uint32_t blank_eeprom_ip = _IP(255,255,255,255); -static const uint32_t default_ip = _IP(192,168,10,3); -static const uint32_t default_dr = _IP(192,168,10,1); -static const uint32_t default_netmask = _IP(255,255,255,0); +#define ETH_BUF_SIZE 512 +uint8_t eth_buf[ETH_BUF_SIZE]; typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len); diff --git a/firmware/octoclock/include/octoclock.h b/firmware/octoclock/include/octoclock.h index 849ab7f96..34cad1c12 100644 --- a/firmware/octoclock/include/octoclock.h +++ b/firmware/octoclock/include/octoclock.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Ettus Research LLC + * Copyright 2014-2015 Ettus Research LLC * * 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 @@ -24,26 +24,21 @@ #include <stdint.h> // Define frequency -#define F_CPU 12500000UL +#define F_CPU 7372800UL /* - * Timer 0 (8-bit) - * * Set prescaler to 8 - * * Enable overflow interrupt - * * Set timer to 0 - */ -#define TIMER0_INIT() TCCR0 = (1 << CS01); \ - TIMSK |= (1 << TOIE0); \ - TCNT0 = 0; -/* * Timer 1 (16-bit) * * Set prescaler to 1024 * * Enable overflow interrupt * * Set timer to 0 */ #define TIMER1_INIT() TCCR1B = (1 << CS12) | (1 << CS10); \ - TIMSK |= (1<<TOIE1); \ - TCNT1 = 0; + TIMSK |= (1<<TOIE1); \ + TCNT1 = 0; + +#define TIMER1_DISABLE() TCCR1B = 0; \ + TIMSK = 0; \ + TCNT1 = 0; #define TIMER1_ONE_SECOND ((uint32_t)(12207)) @@ -94,12 +89,6 @@ * Bits_32(10000000,11111111,10101010,01010101) = 2164238933 */ -typedef enum { - Top, - Middle, - Bottom -} LEDs; - void setup_atmel_io_ports(void); #endif /* _OCTOCLOCK_H_ */ diff --git a/firmware/octoclock/include/state.h b/firmware/octoclock/include/state.h index 9734948cf..2170c2638 100644 --- a/firmware/octoclock/include/state.h +++ b/firmware/octoclock/include/state.h @@ -22,23 +22,24 @@ #include <octoclock.h> -// NOT PRESENT unless proven so... -static ref_t global_which_ref = NO_REF; -static bool global_gps_present = false; -static bool global_ext_ref_is_present = false; +// Global state variables +extern volatile bool g_ext_ref_present; +extern volatile bool g_gps_present; +extern volatile switch_pos_t g_switch_pos; +extern volatile ref_t g_ref; -void led(LEDs which, int turn_it_on); +typedef enum { + LED_TOP, // Internal + LED_MIDDLE, // External + LED_BOTTOM // Status +} led_t; -void LEDs_off(void); +void led(led_t which, bool on); -void force_internal(void); +void leds_off(void); void prefer_internal(void); void prefer_external(void); -ref_t which_ref(void); - -switch_pos_t get_switch_pos(void); - #endif /* _STATE_H_ */ diff --git a/firmware/octoclock/include/usart.h b/firmware/octoclock/include/usart.h index 35ee9eb95..203255988 100644 --- a/firmware/octoclock/include/usart.h +++ b/firmware/octoclock/include/usart.h @@ -25,10 +25,6 @@ void usart_init(void); char usart_getc(void); -char usart_getc_noblock(void); - void usart_putc(char ch); -void usart_putc_nowait(char ch); - #endif /* _USART_H_ */ diff --git a/firmware/octoclock/lib/clkdist.c b/firmware/octoclock/lib/clkdist.c index ed29510b6..0468ac38e 100644 --- a/firmware/octoclock/lib/clkdist.c +++ b/firmware/octoclock/lib/clkdist.c @@ -21,6 +21,8 @@ #include <clkdist.h> #include <state.h> +#include <util/delay.h> + #define wait() for(uint16_t u=14000; u; u--) asm("nop"); #define CLK (PA0) // Shift by 0 bits @@ -33,7 +35,7 @@ // Table of 32-bit constants to be written to the TI chip's registers. These are // from the "Special Settings" on Page 35 of the datasheet. // For the GPS's 10 MHz output -static uint32_t table_Pri_Ref[] = { +static const uint32_t table_Pri_Ref[] = { Bits_32(1,01010100,0,0), // Reg 0 Bits_32(1,01010100,0,0), // Outputs LVCMOS Positive&Negative Active - Non-inverted Bits_32(1,01010100,0,0), @@ -47,7 +49,7 @@ static uint32_t table_Pri_Ref[] = { // For the External 10 MHz input LVDS with external termination, // Effectively DC coupled -static uint32_t table_Sec_Ref[] = { +static const uint32_t table_Sec_Ref[] = { Bits_32(0001,01010100,0,100000), // Reg 0 -- use Secondary Reference for all channels Bits_32(0001,01010100,0,100000), // Outputs LVCMOS Positive&Negative Active - Non-inverted Bits_32(0001,01010100,0,100000), @@ -81,7 +83,6 @@ static bool get_bit(uint8_t bit_number) { // Send 32 bits to TI chip, LSB first. // Don't worry about reading any bits back at this time static void send_SPI(uint32_t bits) { - // Basically, when the clock is low, one can set MOSI to anything, as it's // ignored. set_bit(CE_, Lo); // Start SPI transaction with TI chip @@ -130,7 +131,8 @@ void setup_TI_CDCE18005(TI_Input_10_MHz which_input) { for(uint8_t i=0; i<table_size; i++){ temp = table_Pri_Ref[i]<<4; temp |= i; - send_SPI(temp); // Make sure the register's address is in the LSBs + // Make sure the register's address is in the LSBs + send_SPI(temp); } } else { // is Secondary_Ext -- External 10 MHz input from SMA connector @@ -169,6 +171,10 @@ uint32_t get_TI_CDCE18005(CDCE18005 which_register){ return receive_SPI(); } +void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits){ + send_SPI((bits << 4) | which_register); +} + bool check_TI_CDCE18005(TI_Input_10_MHz which_input, CDCE18005 which_register) { diff --git a/firmware/octoclock/lib/enc28j60.c b/firmware/octoclock/lib/enc28j60.c index f0bbee0e7..ead7e4ec8 100644 --- a/firmware/octoclock/lib/enc28j60.c +++ b/firmware/octoclock/lib/enc28j60.c @@ -1,337 +1,211 @@ -/*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */ -//***************************************************************************** -// -// File Name : 'enc28j60.c' -// Title : Microchip ENC28J60 Ethernet Interface Driver -// Author : Pascal Stang (c)2005 -// Created : 9/22/2005 -// Revised : 5/19/2014 -// Version : 0.1 -// Target MCU : Atmel AVR series -// Editor Tabs : 4 -// -// Description : This driver provides initialization and transmit/receive -// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -// chip, using an SPI interface to the host processor. -// -//***************************************************************************** +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ #include <octoclock.h> #include <net/enc28j60.h> -#include <net/enc28j60conf.h> -#include <avr/io.h> #include <util/delay.h> -u08 Enc28j60Bank; -u16 NextPacketPtr; - -u08 enc28j60ReadOp(u08 op, u08 address) -{ - u08 data; - - // assert CS - ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); - - // issue read command - SPDR = op | (address & ADDR_MASK); - while(!(SPSR & (1<<SPIF))); - // read data - SPDR = 0x00; - while(!(SPSR & (1<<SPIF))); - // do dummy read if needed - if(address & 0x80) - { - SPDR = 0x00; - while(!(inb(SPSR) & (1<<SPIF))); - } - data = SPDR; - - // release CS - ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); - - return data; -} +static uint8_t current_bank; +static uint16_t next_pkt_ptr; -void enc28j60WriteOp(u08 op, u08 address, u08 data) -{ - // assert CS - ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); +#define SET_CS_ACTIVE() SPI_PORT &= ~(1<<SPI_CS); +#define SET_CS_PASSIVE() SPI_PORT |= (1<<SPI_CS); +#define SPI_WAIT() while(!(SPSR & (1<<SPIF))); - // issue write command - SPDR = op | (address & ADDR_MASK); - while(!(SPSR & (1<<SPIF))); - // write data - SPDR = data; - while(!(SPSR & (1<<SPIF))); +static uint8_t enc28j60_read_op(uint8_t op, uint8_t addr){ + SET_CS_ACTIVE(); + SPDR = (op | (addr & ADDR_MASK)); + SPI_WAIT(); + SPDR = 0x00; + SPI_WAIT(); - // release CS - ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); -} + if(addr & 0x80){ + SPDR = 0x00; + SPI_WAIT(); + } -void enc28j60ReadBuffer(u16 len, u08* data) -{ - // assert CS - ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); - - // issue read command - SPDR = ENC28J60_READ_BUF_MEM; - while(!(SPSR & (1<<SPIF))); - while(len--) - { - // read data - SPDR = 0x00; - while(!(SPSR & (1<<SPIF))); - *data++ = SPDR; - } - // release CS - ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); + SET_CS_PASSIVE(); + return SPDR; } -void enc28j60WriteBuffer(u16 len, u08* data) -{ - // assert CS - ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); - - // issue write command - SPDR = ENC28J60_WRITE_BUF_MEM; - while(!(SPSR & (1<<SPIF))); - while(len--) - { - // write data - SPDR = *data++; - while(!(SPSR & (1<<SPIF))); - } - // release CS - ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); +static void enc28j60_write_op(uint8_t op, uint8_t addr, uint8_t value){ + SET_CS_ACTIVE(); + + SPDR = (op | (addr & ADDR_MASK)); + SPI_WAIT(); + SPDR = value; + SPI_WAIT(); + + SET_CS_PASSIVE(); } -void enc28j60SetBank(u08 address) -{ - // set the bank (if needed) - if((address & BANK_MASK) != Enc28j60Bank) - { - // set the bank - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); - Enc28j60Bank = (address & BANK_MASK); - } +static void enc28j60_read_buffer(uint8_t* buf, uint16_t len){ + SET_CS_ACTIVE(); + + SPDR = RBM; + SPI_WAIT(); + while(len){ + len--; + SPDR = 0x00; + SPI_WAIT(); + *buf = SPDR; + buf++; + } + *buf = '\0'; + + SET_CS_PASSIVE(); } -u08 enc28j60Read(u08 address) -{ - // set the bank - enc28j60SetBank(address); - // do the read - return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +static void enc28j60_write_buffer(uint8_t* buf, uint16_t len){ + SET_CS_ACTIVE(); + + SPDR = WBM; + SPI_WAIT(); + while(len){ + len--; + SPDR = *buf; + buf++; + SPI_WAIT(); + } + + SET_CS_PASSIVE(); } -void enc28j60Write(u08 address, u08 data) -{ - // set the bank - enc28j60SetBank(address); - // do the write - enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +static void enc28j60_set_bank(uint8_t addr){ + if((addr & BANK_MASK) != current_bank){ + enc28j60_write_op(BFC, ECON1, (BSEL1|BSEL0)); + enc28j60_write_op(BFS, ECON1, ((addr & BANK_MASK) >> 5)); + current_bank = (addr & BANK_MASK); + } } -u16 enc28j60PhyRead(u08 address) -{ - u16 data; - - // Set the right address and start the register read operation - enc28j60Write(MIREGADR, address); - enc28j60Write(MICMD, MICMD_MIIRD); - - // wait until the PHY read completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY); - - // quit reading - enc28j60Write(MICMD, 0x00); - - // get data value - data = enc28j60Read(MIRDL); - data |= enc28j60Read(MIRDH); - // return the data - return data; +static uint8_t enc28j60_read(uint8_t addr){ + enc28j60_set_bank(addr); + return enc28j60_read_op(RCR, addr); } -void enc28j60PhyWrite(u08 address, u16 data) -{ - // set the PHY register address - enc28j60Write(MIREGADR, address); - - // write the PHY data - enc28j60Write(MIWRL, data); - enc28j60Write(MIWRH, data>>8); - - // wait until the PHY write completes - while(enc28j60Read(MISTAT) & MISTAT_BUSY); +static void enc28j60_write(uint8_t addr, uint16_t value){ + enc28j60_set_bank(addr); + enc28j60_write_op(WCR, addr, value); } -void enc28j60Init(u08* macaddr) -{ - // initialize I/O - sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS); - sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); - - // setup SPI I/O pins - sbi(ENC28J60_SPI_PORT, ENC28J60_SPI_SCK); // set SCK hi - sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SCK); // set SCK as output - cbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MISO); // set MISO as input - sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MOSI); // set MOSI as output - sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SS); // SS must be output for Master mode to work - // initialize SPI interface - // master mode - sbi(SPCR, MSTR); - // select clock phase positive-going in middle of data - cbi(SPCR, CPOL); - // Data order MSB first - cbi(SPCR,DORD); - // switch to f/4 2X = f/2 bitrate - cbi(SPCR, SPR0); - cbi(SPCR, SPR1); - sbi(SPSR, SPI2X); - // enable SPI - sbi(SPCR, SPE); - - // perform system reset - enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - - /* - * "After sending an SPI Reset command, the PHY - * clock is stopped but the ESTAT.CLKRDY bit is not - * cleared. Therefore, polling the CLKRDY bit will not - * work to detect if the PHY is ready. - * - * Additionally, the hardware start-up time of 300 us - * may expire before the device is ready to operate. - * - * Work around - * After issuing the Reset command, wait at least - * 1 ms in firmware for the device to be ready." - * - * Source: http://ww1.microchip.com/downloads/en/DeviceDoc/80349c.pdf - */ - _delay_ms(1); - - // do bank 0 stuff - // initialize receive buffer - // 16-bit transfers, must write low byte first - // set receive buffer start address - NextPacketPtr = RXSTART_INIT; - enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); - enc28j60Write(ERXSTH, RXSTART_INIT>>8); - // set receive pointer address - enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); - enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); - // set receive buffer end - // ERXND defaults to 0x1FFF (end of ram) - enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); - enc28j60Write(ERXNDH, RXSTOP_INIT>>8); - // set transmit buffer start - // ETXST defaults to 0x0000 (beginnging of ram) - enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); - enc28j60Write(ETXSTH, TXSTART_INIT>>8); - - // do bank 2 stuff - // enable MAC receive - enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); - // bring MAC out of reset - enc28j60Write(MACON2, 0x00); - // enable automatic padding and CRC operations - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); - // set inter-frame gap (non-back-to-back) - enc28j60Write(MAIPGL, 0x12); - enc28j60Write(MAIPGH, 0x0C); - // set inter-frame gap (back-to-back) - enc28j60Write(MABBIPG, 0x12); - // Set the maximum packet size which the controller will accept - enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF); - enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); - - // do bank 3 stuff - // write MAC address - // NOTE: MAC address in ENC28J60 is byte-backward - enc28j60Write(MAADR5, macaddr[0]); - enc28j60Write(MAADR4, macaddr[1]); - enc28j60Write(MAADR3, macaddr[2]); - enc28j60Write(MAADR2, macaddr[3]); - enc28j60Write(MAADR1, macaddr[4]); - enc28j60Write(MAADR0, macaddr[5]); - - // no loopback of transmitted frames - enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); - - // switch to bank 0 - enc28j60SetBank(ECON1); - // enable interrutps - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); - // enable packet reception - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +void enc28j60_init(uint8_t* mac_addr){ + SPI_DDR |= (1 << SPI_CS); + SET_CS_PASSIVE(); + + SPI_DDR |= ((1 << SPI_MOSI) | (1 << SPI_SCK)); + SPI_DDR &= ~(1 << SPI_MISO); + SPI_PORT &= ~(1 << SPI_MOSI); + SPI_PORT &= ~(1 << SPI_SCK); + SPCR = ((1 << SPE) | (1 << MSTR)); + SPSR |= (1 << SPI2X); + enc28j60_write_op(SC, 0, SC); + next_pkt_ptr = RXSTART_INIT; + + // Designate RX addresses + enc28j60_write(ERXSTL, (RXSTART_INIT & 0xFF)); + enc28j60_write(ERXSTH, (RXSTART_INIT >> 8)); + enc28j60_write(ERXNDL, (RXSTOP_INIT & 0xFF)); + enc28j60_write(ERXNDH, (RXSTOP_INIT >> 8)); + + // Designate TX addresses + enc28j60_write(ETXSTL, (TXSTART_INIT & 0xFF)); + enc28j60_write(ETXSTH, (TXSTART_INIT >> 8)); + enc28j60_write(ETXNDL, (TXSTOP_INIT & 0xFF)); + enc28j60_write(ETXNDH, (TXSTOP_INIT >> 8)); + + // Configure filters + enc28j60_write(ERXFCON, (UCEN|CRCEN|PMEN|BCEN)); + enc28j60_write(EPMM0, 0x3F); + enc28j60_write(EPMM1, 0x30); + enc28j60_write(EPMCSL, 0xF9); + enc28j60_write(EPMCSH, 0xF7); + + // MAC initialization + enc28j60_write(MACON1, (MARXEN|TXPAUS|RXPAUS)); + enc28j60_write(MACON2, 0x00); + enc28j60_write_op(BFS, MACON3, (PADCFG0|TXCRCEN|FRMLNEN)); + enc28j60_write(MAIPGL, 0x12); + enc28j60_write(MAIPGH, 0x0C); + enc28j60_write(MABBIPG, 0x12); + enc28j60_write(MAMXFLL, (MAX_FRAMELEN & 0xFF)); + enc28j60_write(MAMXFLH, (MAX_FRAMELEN >> 8)); + enc28j60_write(MAADR5, mac_addr[0]); + enc28j60_write(MAADR4, mac_addr[1]); + enc28j60_write(MAADR3, mac_addr[2]); + enc28j60_write(MAADR2, mac_addr[3]); + enc28j60_write(MAADR1, mac_addr[4]); + enc28j60_write(MAADR0, mac_addr[5]); + + enc28j60_set_bank(ECON1); + enc28j60_write_op(BFS, ECON1, ENCRXEN); } -void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2) -{ - //Errata: Transmit Logic reset - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); - enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - - // Set the write pointer to start of transmit buffer area - enc28j60Write(EWRPTL, TXSTART_INIT&0xff); - enc28j60Write(EWRPTH, TXSTART_INIT>>8); - // Set the TXND pointer to correspond to the packet size given - enc28j60Write(ETXNDL, (TXSTART_INIT+len1+len2)); - enc28j60Write(ETXNDH, (TXSTART_INIT+len1+len2)>>8); - - // write per-packet control byte - enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); - - // copy the packet into the transmit buffer - enc28j60WriteBuffer(len1, packet1); - if(len2>0) enc28j60WriteBuffer(len2, packet2); - - // send the contents of the transmit buffer onto the network - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +uint16_t enc28j60_recv(uint8_t* buf, uint16_t max_len){ + uint16_t rxstat, len; + + // Return if no data is available + if(enc28j60_read(EPKTCNT) == 0) return 0; + + enc28j60_write(ERDPTL, (next_pkt_ptr & 0xFF)); + enc28j60_write(ERDPTH, (next_pkt_ptr >> 8)); + next_pkt_ptr = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); + len = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); + len -= 4; + rxstat = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); + + // Length sanity check and actual enc28j60_read call + if(len > (max_len - 1)) len = max_len - 1; + if((rxstat & 0x80) == 0) len = 0; + else enc28j60_read_buffer(buf, len); + + // Update next packet pointer + enc28j60_write(ERXRDPTL, (next_pkt_ptr & 0xFF)); + enc28j60_write(ERXRDPTH, (next_pkt_ptr >> 8)); + if(((next_pkt_ptr - 1) < RXSTART_INIT) || ((next_pkt_ptr - 1) > RXSTOP_INIT)){ + enc28j60_write(ERXRDPTL, (RXSTOP_INIT & 0xFF)); + enc28j60_write(ERXRDPTH, (RXSTOP_INIT >> 8)); + } + else{ + enc28j60_write(ERXRDPTL, ((next_pkt_ptr - 1) & 0xFF)); + enc28j60_write(ERXRDPTH, ((next_pkt_ptr - 1) >> 8)); + } + enc28j60_write_op(BFS, ECON2, PKTDEC); + + return len; } -unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf) -{ - u16 rxstat; - u16 len; - - // check if a packet has been received and buffered - if( !enc28j60Read(EPKTCNT) ) - return 0; - - // Set the read pointer to the start of the received packet - enc28j60Write(ERDPTL, (NextPacketPtr)); - enc28j60Write(ERDPTH, (NextPacketPtr)>>8); - // read the next packet pointer - NextPacketPtr = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - // read the packet length - len = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - // read the receive status - rxstat = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); - rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - - // limit retrieve length - // (we reduce the MAC-reported length by 4 to remove the CRC) - len = MIN(len, maxlen); - - // copy the packet from the receive buffer - enc28j60ReadBuffer(len, buf); - - // Move the RX read pointer to the start of the next received packet - // This frees the memory we just read out - enc28j60Write(ERXRDPTL, (NextPacketPtr)); - enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8); - - // decrement the packet counter indicate we are done with this packet - enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - - return len; +void enc28j60_send(uint8_t* buf, uint16_t len){ + + // Wait for any current transmission to finish + while(enc28j60_read_op(RCR, ECON1) & TXRTS){ + if(enc28j60_read(EIR) & TXERIF){ + enc28j60_write_op(BFS, ECON1, TXRST); + enc28j60_write_op(BFC, ECON1, TXRST); + } + } + + enc28j60_write(EWRPTL, (TXSTART_INIT & 0xFF)); + enc28j60_write(EWRPTH, (TXSTART_INIT >> 8)); + enc28j60_write(ETXNDL, ((TXSTART_INIT + len) & 0xFF)); + enc28j60_write(ETXNDH, ((TXSTART_INIT + len) >> 8)); + enc28j60_write_op(WBM, 0, 0x00); + enc28j60_write_buffer(buf, len); + enc28j60_write_op(BFS, ECON1, TXRTS); } diff --git a/firmware/octoclock/lib/init.c b/firmware/octoclock/lib/init.c index 827ccb376..8592aa091 100644 --- a/firmware/octoclock/lib/init.c +++ b/firmware/octoclock/lib/init.c @@ -71,7 +71,7 @@ void setup_atmel_io_ports(){ // /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start // all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs -PORTA = Bits_8(00110010); +PORTA = Bits_8(00100010); DDRA = 1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0; /* @@ -90,8 +90,8 @@ DDRA = 1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0; * */ -PORTB = Bits_8(01100001); // Initial Value is all zeros -DDRB = 1<<DDB2 | 1<<DDB4 | 1<<DDB7; // MOSI is an output; the Not Connected pins are also outputs +PORTB = Bits_8(01100001); // Initial Value is all zeros +DDRB = Bits_8(11110111); // MOSI is an output; the Not Connected pins are also outputs /* * Port C diff --git a/firmware/octoclock/lib/network.c b/firmware/octoclock/lib/network.c index bb49de4f6..22fc2b54b 100644 --- a/firmware/octoclock/lib/network.c +++ b/firmware/octoclock/lib/network.c @@ -27,6 +27,7 @@ #include <debug.h> #include <octoclock.h> +#include <state.h> #include <network.h> #include <net/enc28j60.h> @@ -34,12 +35,14 @@ #include <net/if_arp.h> #include <net/ethertype.h> +#include <util/delay.h> + #include "arp_cache.h" /*********************************************************************** * Constants + Globals **********************************************************************/ -static const size_t out_buff_size = 512; +static const size_t out_buff_size = ETH_BUF_SIZE; static const eth_mac_addr_t BCAST_MAC_ADDR = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}; #define MAX_UDP_LISTENERS 10 @@ -139,7 +142,7 @@ send_pkt( //grab an out buffer and pointer //select the output buffer based on type of packet uint8_t *p; - p = buf_out; + p = eth_buf; size_t total_len = 0; //create a list of all buffers to copy @@ -149,7 +152,7 @@ send_pkt( //copy each buffer into the out buffer for (size_t i = 0; i < sizeof(buffs)/sizeof(buffs[0]); i++){ total_len += lens[i]; //use full length (not clipped) - size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)buf_out); + size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)eth_buf); if (lens[i] > bytes_remaining) lens[i] = bytes_remaining; memcpy(p, buffs[i], lens[i]); p += lens[i]; @@ -161,7 +164,7 @@ send_pkt( //For some reason, the ENC28J60 won't send the CRC //if you don't tell it to send another byte after //the given packet - enc28j60PacketSend(total_len+1, buf_out, 0, 0); + enc28j60_send(eth_buf, total_len); } static void @@ -333,15 +336,15 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size) void handle_eth_packet(size_t recv_len) { - eth_hdr_t *eth_hdr = (eth_hdr_t *)buf_in; + eth_hdr_t *eth_hdr = (eth_hdr_t *)eth_buf; uint16_t ethertype = htons(eth_hdr->ethertype); if (ethertype == ETHERTYPE_ARP){ - struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(buf_in + sizeof(eth_hdr_t)); + struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(eth_buf + sizeof(eth_hdr_t)); handle_arp_packet(arp, recv_len-ETH_HLEN); } else if (ethertype == ETHERTYPE_IPV4){ - struct ip_hdr *ip = (struct ip_hdr *)(buf_in + sizeof(eth_hdr_t)); + struct ip_hdr *ip = (struct ip_hdr *)(eth_buf + sizeof(eth_hdr_t)); if (_IPH_V(ip) != 4 || _IPH_HL(ip) != 5) // ignore pkts w/ bad version or options return; @@ -357,7 +360,7 @@ handle_eth_packet(size_t recv_len) bool is_my_ip = memcmp(&ip->dest, &htonl_local_ip_addr, sizeof(_local_ip_addr)) == 0; if (!is_bcast && !is_my_ip) return; - arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)buf_in)+6)); + arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)eth_buf)+6)); switch (_IPH_PROTO(ip)){ case IP_PROTO_UDP: @@ -381,7 +384,6 @@ handle_eth_packet(size_t recv_len) **********************************************************************/ static bool send_garp = false; -static bool sent_initial_garp = false; static uint32_t num_overflows = 0; // Six overflows is the closest overflow count to one minute. @@ -390,6 +392,7 @@ ISR(TIMER1_OVF_vect){ if(!(num_overflows % 6)) send_garp = true; } +// Send a GARP packet once per minute. static void send_gratuitous_arp(){ send_garp = false; @@ -415,18 +418,9 @@ send_gratuitous_arp(){ // Executed every loop void network_check(void){ - size_t recv_len = enc28j60PacketReceive(512, buf_in); + size_t recv_len = enc28j60_recv(eth_buf, ETH_BUF_SIZE); if(recv_len > 0) handle_eth_packet(recv_len); - /* - * Send a gratuitous ARP packet two seconds after Ethernet - * initialization. - */ - if(!sent_initial_garp && (num_overflows == 0 && TCNT1 > (TIMER1_ONE_SECOND*2))){ - sent_initial_garp = true; - send_garp = true; - } - if(send_garp) send_gratuitous_arp(); } @@ -435,9 +429,10 @@ void network_init(void){ * Read MAC address from EEPROM and initialize Ethernet driver. If EEPROM is blank, * use default MAC address instead. */ + eeprom_busy_wait(); if(eeprom_read_byte(0) == 0xFF){ _MAC_ADDR(_local_mac_addr.addr, 0x00,0x80,0x2F,0x11,0x22,0x33); - _local_ip_addr.addr = default_ip; + _local_ip_addr.addr = _IP(192,168,10,3); using_network_defaults = true; } else{ @@ -446,5 +441,8 @@ void network_init(void){ using_network_defaults = false; } - enc28j60Init((uint8_t*)&_local_mac_addr); + enc28j60_init((uint8_t*)&_local_mac_addr); + init_udp_listeners(); + + send_garp = true; } diff --git a/firmware/octoclock/lib/state.c b/firmware/octoclock/lib/state.c index 0dbcc6ece..26e1b783c 100644 --- a/firmware/octoclock/lib/state.c +++ b/firmware/octoclock/lib/state.c @@ -15,110 +15,80 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <avr/interrupt.h> #include <avr/io.h> +#include <avrlibdefs.h> #include <debug.h> #include <octoclock.h> #include <clkdist.h> #include <state.h> -void led(LEDs which, int turn_it_on) { +// Global state variables +volatile bool g_ext_ref_present = false; +volatile bool g_gps_present = false; +volatile switch_pos_t g_switch_pos = PREFER_INTERNAL; +volatile ref_t g_ref = NO_REF; +void led(led_t which, bool on){ // selects the proper bit uint8_t LED = 0x20 << which; - if(turn_it_on) + if(on) PORTC |= LED; else PORTC &= ~LED; } -void LEDs_Off(void){ - led(Top,false); - led(Middle,false); - led(Bottom,false); +void leds_off(void){ + led(LED_TOP, false); + led(LED_MIDDLE, false); + led(LED_BOTTOM, false); } -void force_internal(void){ - led(Top,true); - led(Middle,false); - led(Bottom,true); +static void force_internal(void){ + led(LED_TOP, true); + led(LED_MIDDLE, false); + led(LED_BOTTOM, true); + // Tell ClkDist chip to use internal signals + cli(); setup_TI_CDCE18005(Primary_GPS); + sei(); - // Set PPS to Primary (1) n.b.: "1" in general means "Internal" for all - // such signals + // Set PPS to internal PORTA |= (1<<PA6); } -void force_external(void){ - led(Top, false); - led(Middle, true); - led(Bottom, true); +static void force_external(void){ + led(LED_TOP, false); + led(LED_MIDDLE, true); + led(LED_BOTTOM, true); + // Tell Clkdist chip to use external signals + cli(); setup_TI_CDCE18005(Secondary_Ext); + sei(); - // Set PPS to External + // Set PPS to external PORTA &= ~(1<<PA6); } void prefer_internal(void){ - // if internal is NOT OK, then force external - if(global_gps_present) + // If internal is NOT OK, then force external + if(g_gps_present) force_internal(); - else if(global_ext_ref_is_present) + else if(g_ext_ref_present) force_external(); else - LEDs_Off(); + leds_off(); } void prefer_external(void){ - // if external is NOT OK, then force internal - if(global_ext_ref_is_present) + // If external is NOT OK, then force internal + if(g_ext_ref_present) force_external(); - else if(global_gps_present) + else if(g_gps_present) force_internal(); else - LEDs_Off(); -} - -static uint8_t prev_PE7 = 0; -static uint32_t timer0_num_overflows = 0; - -ISR(TIMER0_OVF_vect){ - global_gps_present = (PIND & (1<<DDD4)); - - // Every ~1/10 second - if(!(timer0_num_overflows % 610)){ - prev_PE7 = (PINE & (1<<DDE7)); - - if(get_switch_pos() == UP) prefer_internal(); - else prefer_external(); - - global_ext_ref_is_present = false; - } - - if(!global_ext_ref_is_present){ - global_ext_ref_is_present = (prev_PE7 != (PINE & (1<<DDE7))); - } - - timer0_num_overflows++; -} - -ref_t which_ref(void){ - if(!global_gps_present && !global_ext_ref_is_present) global_which_ref = NO_REF; - else if(global_gps_present && !global_ext_ref_is_present) global_which_ref = INTERNAL; - else if(!global_gps_present && global_ext_ref_is_present) global_which_ref = EXTERNAL; - else global_which_ref = (get_switch_pos() == UP) ? INTERNAL : EXTERNAL; - - return global_which_ref; -} - -switch_pos_t get_switch_pos(void){ - uint8_t portC = PINC; - - // UP is prefer internal, - // DOWN is prefer external - return (portC & (1<<DDC1)) ? DOWN : UP; + leds_off(); } diff --git a/firmware/octoclock/lib/udp_handlers.c b/firmware/octoclock/lib/udp_handlers.c index 1f20112c9..49b9b8023 100644 --- a/firmware/octoclock/lib/udp_handlers.c +++ b/firmware/octoclock/lib/udp_handlers.c @@ -36,7 +36,7 @@ void handle_udp_ctrl_packet( pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM; pkt_out.sequence = pkt_in->sequence; - //If the firmware is incompatible, only respond to queries + // If the firmware is incompatible, only respond to queries if(pkt_in->code == OCTOCLOCK_QUERY_CMD){ pkt_out.code = OCTOCLOCK_QUERY_ACK; pkt_out.len = 0; @@ -50,44 +50,43 @@ void handle_udp_ctrl_packet( octoclock_fw_eeprom_t *eeprom_info = (octoclock_fw_eeprom_t*)pkt_out.data; - //Read values from EEPROM into packet + // Read values from EEPROM into packet + eeprom_busy_wait(); eeprom_read_block(eeprom_info, 0, sizeof(octoclock_fw_eeprom_t)); - //If EEPROM network fields are not fully populated, copy defaults + // If EEPROM network fields are not fully populated, copy defaults if(using_network_defaults){ _MAC_ADDR(eeprom_info->mac_addr, 0x00,0x80,0x2F,0x11,0x22,0x33); - eeprom_info->ip_addr = default_ip; - eeprom_info->dr_addr = default_dr; - eeprom_info->netmask = default_netmask; + eeprom_info->ip_addr = _IP(192,168,10,3); + eeprom_info->dr_addr = _IP(192,168,10,1); + eeprom_info->netmask = _IP(255,255,255,0); } - //Check if strings or revision is empty - if(eeprom_info->serial[0] == 0xFF) memset(eeprom_info->serial, 0, 10); - if(eeprom_info->name[0] == 0xFF) memset(eeprom_info->name, 0, 10); + // Check if strings or revision is empty if(eeprom_info->revision == 0xFF) eeprom_info->revision = 0; break; case BURN_EEPROM_CMD:{ - //Confirm length of data + // Confirm length of data if(pkt_in->len != sizeof(octoclock_fw_eeprom_t)){ pkt_out.code = BURN_EEPROM_FAILURE_ACK; break; } /* - * In all cases, a full octoclock_fw_eeprom_t is written to lower the overall - * number of writes due to this EEPROM's smaller amount of safe writes. * It is up to the host to make sure that the values that should be * preserved are present in the octoclock_fw_eeprom_t struct. */ const octoclock_fw_eeprom_t *eeprom_pkt = (octoclock_fw_eeprom_t*)pkt_in->data; pkt_out.len = 0; - //Write EEPROM data from packet + // Write EEPROM data from packet + eeprom_busy_wait(); eeprom_write_block(eeprom_pkt, 0, sizeof(octoclock_fw_eeprom_t)); - //Read back and compare to packet to confirm successful write + // Read back and compare to packet to confirm successful write uint8_t eeprom_contents[sizeof(octoclock_fw_eeprom_t)]; + eeprom_busy_wait(); eeprom_read_block(eeprom_contents, 0, sizeof(octoclock_fw_eeprom_t)); uint8_t n = memcmp(eeprom_contents, eeprom_pkt, sizeof(octoclock_fw_eeprom_t)); pkt_out.code = n ? BURN_EEPROM_FAILURE_ACK @@ -99,12 +98,12 @@ void handle_udp_ctrl_packet( pkt_out.code = SEND_STATE_ACK; pkt_out.len = sizeof(octoclock_state_t); - //Populate octoclock_state_t fields + // Populate octoclock_state_t fields octoclock_state_t *state = (octoclock_state_t*)pkt_out.data; - state->external_detected = global_ext_ref_is_present ? 1 : 0; - state->gps_detected = (PIND & _BV(DDD4)) ? 1 : 0; - state->which_ref = (uint8_t)which_ref(); - state->switch_pos = (uint8_t)get_switch_pos(); + state->external_detected = g_ext_ref_present ? 1 : 0; + state->gps_detected = g_gps_present ? 1 : 0; + state->which_ref = (uint8_t)g_ref; + state->switch_pos = (uint8_t)g_switch_pos; break; case RESET_CMD: diff --git a/firmware/octoclock/lib/usart.c b/firmware/octoclock/lib/usart.c index 3620ac5e9..a267e43c6 100644 --- a/firmware/octoclock/lib/usart.c +++ b/firmware/octoclock/lib/usart.c @@ -34,16 +34,8 @@ char usart_getc(void){ return UDR1; } -char usart_getc_noblock(void){ - return ((UCSR1A & (1 << RXC))) ? UDR1 : -1; -} - void usart_putc(char ch){ while((UCSR1A & (1 << UDRE1)) == 0); UDR1 = ch; } - -void usart_putc_nowait(char ch){ - if((UCSR1A & (1 << UDRE1)) != 0) UDR1 = ch; -} diff --git a/firmware/octoclock/octoclock_r4/CMakeLists.txt b/firmware/octoclock/octoclock_r4/CMakeLists.txt index c3559d8d4..9223721c8 100644 --- a/firmware/octoclock/octoclock_r4/CMakeLists.txt +++ b/firmware/octoclock/octoclock_r4/CMakeLists.txt @@ -43,7 +43,7 @@ add_custom_target( ) add_custom_target( upload_r4 - ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xFF:m -U flash:w:octoclock_r4_fw.hex:i + ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i WORKING_DIRECTORY ${CMAKE_BINARY_DIR} DEPENDS octoclock_r4_fw_hex COMMENT "Uploading OctoClock firmware to device with ${PROGRAMMER}" diff --git a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c index 5e8e6d09b..b1df34195 100644 --- a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c +++ b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c @@ -36,7 +36,7 @@ * JTAGEN = [X] * SPIEN = [X] * EESAVE = [X] - * BOOTSZ = 4096W_F000 + * BOOTSZ = 4095W_F000 * BOOTRST = [ ] * CKOPT = [X] * BODLEVEL = 2V7 @@ -49,57 +49,165 @@ * */ +#include <stdlib.h> #include <string.h> #include <stdint.h> #include <stdbool.h> #include <avr/eeprom.h> +#include <avr/interrupt.h> #include <avr/io.h> +#include <avrlibdefs.h> #include <octoclock.h> -#include <clkdist.h> #include <debug.h> +#include <clkdist.h> #include <state.h> #include <network.h> #include <usart.h> -#include <gpsdo.h> -#include <net/enc28j60.h> #include <net/udp_handlers.h> +/* + * Timer 3 (16-bit) + * * Set input capture trigger to rising edge + * * Set prescaler to 1 + * * Enable overflow interrupt + * * Set timer to 0 + */ +#define TIMER3_INIT() TCCR3B = (1 << ICES3) | (1 << CS30); \ + ETIMSK |= (1 << TOIE3); \ + TCNT3 = 0; \ + ICR3 = 0; + +/* + * We use TIMER3 as a watchdog timer for external reference + * detection. Once a signal is detected, we allow for five + * timer overflows (~26 ms) without another signal before + * deciding that there is no external reference connected. + */ +#define EXT_REF_TIMEOUT 5 + +static volatile uint16_t num_overflows = 0; +static uint16_t current_num_overflows = 0; +static uint16_t prev_num_overflows = 0; +static uint16_t current_ICR3 = 0; +static uint16_t prev_ICR3 = 0; +static ref_t prev_ref = NO_REF; +static switch_pos_t prev_switch_pos = PREFER_EXTERNAL; +bool top = false; + +ISR(TIMER3_OVF_vect){ + num_overflows++; +} + /******************************************************************************* * Main Routine *******************************************************************************/ int main(void){ - asm("cli"); + /* + * Initializations + */ + cli(); + // Make sure interrupts belong to us + MCUCR = (1<<IVCE); + MCUCR = 0; + + // Initialize global variables + g_ext_ref_present = false; + g_gps_present = false; + g_switch_pos = PREFER_INTERNAL; + g_ref = NO_REF; + + // Atmega128 setup_atmel_io_ports(); - network_init(); - #ifndef DEBUG - asm("sei"); - #endif + // Reset ClkDist chip + reset_TI_CDCE18005(); - init_udp_listeners(); - register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_ctrl_packet); + // GPSDO communication + usart_init(); + + // Ethernet stack + network_init(); + register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_ctrl_packet); register_udp_listener(OCTOCLOCK_UDP_GPSDO_PORT, handle_udp_gpsdo_packet); - DEBUG_INIT(); // Does nothing when not in debug mode - DEBUG_LOG(" "); //Force a newline between runs - usart_init(); + // Timers + TIMER1_INIT(); // Network + TIMER3_INIT(); // External reference check + + // Debug (does nothing when not in debug mode) + DEBUG_INIT(); + DEBUG_LOG(" "); // Force a newline between runs + + leds_off(); + + sei(); + + // Check if GPS present (should only need to happen once) + g_gps_present = (PIND & (1<<DDD4)); + + // State of previous iteration + prev_ref = NO_REF; + prev_switch_pos = PREFER_EXTERNAL; + cli(); + prev_ICR3 = ICR3; + sei(); + prev_num_overflows = 0; + + /* + * Main loop + */ + while(true){ + // Check switch position + g_switch_pos = (PINC & (1<<DDC1)) ? PREFER_EXTERNAL : PREFER_INTERNAL; + + /* + * Check input capture pin for external reference detection. + * + * 16-bit reads could be corrupted during an interrupt, so + * disable interrupts for safety. + */ + cli(); + current_ICR3 = ICR3; + current_num_overflows = num_overflows; + sei(); + + // Signal detected, reset timer + if(current_ICR3 != prev_ICR3){ + cli(); + TCNT3 = 0; + num_overflows = 0; + sei(); + g_ext_ref_present = true; + } + + // Timeout, no external reference detected + if(current_num_overflows >= EXT_REF_TIMEOUT){ + g_ext_ref_present = false; + } - //Set initial ClkDist and front panel settings - led(Middle,true); - setup_TI_CDCE18005(Primary_GPS); // 10 MHz from Internal Source + // Determine and set reference based on state + if(!g_gps_present && !g_ext_ref_present) g_ref = NO_REF; + else if(g_gps_present && !g_ext_ref_present) g_ref = INTERNAL; + else if(!g_gps_present && g_ext_ref_present) g_ref = EXTERNAL; + else g_ref = (g_switch_pos == PREFER_INTERNAL) ? INTERNAL : EXTERNAL; - led(Top,true); - PORTA |= (1<<PA6); // PPS from Internal source + if((g_ref != prev_ref) || (g_switch_pos != prev_switch_pos)){ + if(g_switch_pos == PREFER_INTERNAL) prefer_internal(); + else prefer_external(); + } - TIMER0_INIT(); - TIMER1_INIT(); + // Record this iteration's state + prev_ref = g_ref; + prev_switch_pos = g_switch_pos; + prev_ICR3 = current_ICR3; + prev_num_overflows = current_num_overflows; - while(true) { + // Handle incoming Ethernet packets network_check(); } } diff --git a/firmware/usrp3/CMakeLists.txt b/firmware/usrp3/CMakeLists.txt index c25adb68a..f71b79b0c 100644 --- a/firmware/usrp3/CMakeLists.txt +++ b/firmware/usrp3/CMakeLists.txt @@ -68,6 +68,20 @@ FIND_PROGRAM(OBJDUMP zpu-elf-objdump) FIND_PROGRAM(HEXDUMP hexdump) ######################################################################## +# Firmware tracing support +######################################################################## +# Look at include/trace.h to see what the different trace levels map to. +SET(TRACE_LEVEL "0" CACHE STRING "Firmware Trace Level") #0 by default +OPTION(TRACE_LEVEL "Firmware Trace Level" "") +IF(TRACE_LEVEL) + #If TRACE_LEVEL == 0, don't define UHD_FW_TRACE_LEVEL so that the C + #code can easily detect if tracing is requested + IF(${TRACE_LEVEL} GREATER 0) + ADD_DEFINITIONS(-DUHD_FW_TRACE_LEVEL=${TRACE_LEVEL}) + ENDIF(${TRACE_LEVEL} GREATER 0) +ENDIF(TRACE_LEVEL) + +######################################################################## # helper functions to build output formats ######################################################################## SET(GEN_OUTPUTS_BIN_SIZE "bin_size_not_set") #set before calling diff --git a/firmware/usrp3/include/ethernet.h b/firmware/usrp3/include/ethernet.h index 52f14d05b..a6bacfcd0 100644 --- a/firmware/usrp3/include/ethernet.h +++ b/firmware/usrp3/include/ethernet.h @@ -31,7 +31,7 @@ typedef void (*ethernet_link_changed_callback_t)(int ethnum, int speed); /*! * \brief one time call to initialize ethernet */ -void xge_ethernet_init(const uint32_t eth); +void ethernet_init(const uint32_t eth); /*! * \brief Return number of ethernet interfaces @@ -44,8 +44,7 @@ void dump_mdio_regs(const uint8_t eth, uint32_t mdio_port); /*! * \brief Test status of SFP+ modules */ -void -xge_poll_sfpp_status(const uint32_t eth); +void poll_sfpp_status(const uint32_t eth); //! get the link status of eth (true for link up) bool ethernet_get_link_up(const uint32_t eth); diff --git a/firmware/usrp3/include/trace.h b/firmware/usrp3/include/trace.h new file mode 100644 index 000000000..0daa231fe --- /dev/null +++ b/firmware/usrp3/include/trace.h @@ -0,0 +1,82 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_TRACE_H +#define INCLUDED_TRACE_H + +#include <stdint.h> +#include <stdbool.h> +#include <printf.h> + +/* + * Enables basic conditional tracing support + * If UHD_FW_TRACE_LEVEL is defined, all messages + * with a verbosity >= UHD_FW_TRACE_LEVEL will be + * printed. + * + * An alternate way of defining the level is the "TRACE_LEVEL" + * variable in cmake. (eg. -DTRACE_LEVEL=13). + */ +//#define UHD_FW_TRACE_LEVEL 13 + +typedef enum +{ + /* 0-9: Use for performance/restricted debugging */ + ERROR = 10, + WARN = 11, + INFO = 12, + /* 13-19: Use for general debugging */ + DEBUG = 20, //Verbose! +} trace_level_t; + +static inline int _trace_typecheck_conv(trace_level_t lvl) { + return (int)lvl; +} + +#define UHD_FW_PRINTF(...) printf(__VA_ARGS__) +#define UHD_FW_BEAUTIFY_LVL(lvl) "[" #lvl "] " + +/* + * UHD_FW_TRACE(<log level>, <message>) + * - Simple trace. Print the messages with the log level as the prefix and \n at the end + * + * UHD_FW_TRACE_FSTR(<log level>, <format string>, <args>) + * - Trace format string with the log level as the prefix and \n at the end + * + * UHD_FW_TRACE_SHORT(<log level>, <message>) + * - Simple trace. Print the messages without the log level prefix or \n at the end + * + * UHD_FW_TRACE_FSTR_SHORT(<log level>, <format string>, <args>) + * - Trace format string without the log level prefix or \n at the end + */ +#ifdef UHD_FW_TRACE_LEVEL + #define UHD_FW_TRACE(lvl, fmt) \ + if (UHD_FW_TRACE_LEVEL >= _trace_typecheck_conv(lvl)) UHD_FW_PRINTF(UHD_FW_BEAUTIFY_LVL(lvl) fmt "\r\n"); + #define UHD_FW_TRACE_FSTR(lvl, fmt, ...) \ + if (UHD_FW_TRACE_LEVEL >= _trace_typecheck_conv(lvl)) UHD_FW_PRINTF(UHD_FW_BEAUTIFY_LVL(lvl) fmt "\r\n", __VA_ARGS__); + #define UHD_FW_TRACE_SHORT(lvl, fmt) \ + if (UHD_FW_TRACE_LEVEL >= _trace_typecheck_conv(lvl)) UHD_FW_PRINTF(fmt); + #define UHD_FW_TRACE_FSTR_SHORT(lvl, fmt, ...) \ + if (UHD_FW_TRACE_LEVEL >= _trace_typecheck_conv(lvl)) UHD_FW_PRINTF(fmt, __VA_ARGS__); +#else + #define UHD_FW_TRACE(lvl, fmt) ; + #define UHD_FW_TRACE_FSTR(lvl, fmt, ...) ; + #define UHD_FW_TRACE_SHORT(lvl, fmt) ; + #define UHD_FW_TRACE_FSTR_SHORT(lvl, fmt, ...) ; +#endif + +#endif /* INCLUDED_TRACE_H */ diff --git a/firmware/usrp3/lib/ethernet.c b/firmware/usrp3/lib/ethernet.c index 7a86980c7..91efbfe1d 100644 --- a/firmware/usrp3/lib/ethernet.c +++ b/firmware/usrp3/lib/ethernet.c @@ -22,7 +22,7 @@ #include "../x300/x300_defs.h" #include "ethernet.h" #include "mdelay.h" -#include "printf.h" +#include <trace.h> #include "wb_i2c.h" #include "wb_utils.h" //#include "memory_map.h" @@ -225,7 +225,7 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms) x = xge_i2c_rd(base, MODULE_DEV_ADDR, 3); // I2C Error? if (x < 0) { - printf("DEBUG: I2C error in SFPP_TYPE.\n"); + UHD_FW_TRACE(ERROR, "I2C error in SFPP_TYPE."); return x; } // Decode module type. These registers and values are defined in SFF-8472 @@ -235,55 +235,55 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms) } if (x & 0x10) { - printf("DEBUG: SFFP_TYPE_SR.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_SR."); return SFFP_TYPE_SR; } if (x & 0x20) { - printf("DEBUG: SFFP_TYPE_LR.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_LR."); return SFFP_TYPE_LR; } if (x & 0x40) { - printf("DEBUG: SFFP_TYPE_LRM.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_LRM."); return SFFP_TYPE_LRM; } // Search for legacy 1000-Base SFP types x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0x6); if (x < 0) { - printf("DEBUG: I2C error in SFPP_TYPE.\n"); + UHD_FW_TRACE(ERROR, "I2C error in SFPP_TYPE."); return x; } if (x & 0x01) { - printf("DEBUG: SFFP_TYPE_1000BASE_SX.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_1000BASE_SX."); return SFFP_TYPE_1000BASE_SX; } if (x & 0x02) { - printf("DEBUG: SFFP_TYPE_1000BASE_LX.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_1000BASE_LX."); return SFFP_TYPE_1000BASE_LX; } if (x & 0x08) { - printf("DEBUG: SFFP_TYPE_1000BASE_T.\n"); + UHD_FW_TRACE(DEBUG, "SFFP_TYPE_1000BASE_T."); return SFFP_TYPE_1000BASE_T; } // Not one of the standard optical types..now try to deduce if it's twinax aka 10GSFP+CU // which is not covered explicitly in SFF-8472 x = xge_i2c_rd(base, MODULE_DEV_ADDR, 8); if (x < 0) { - printf("DEBUG: I2C error in SFPP_TYPE.\n"); + UHD_FW_TRACE(ERROR, "I2C error in SFPP_TYPE."); return x; } if ((x & 4) == 0) // Passive SFP+ cable type goto unknown; // x = xge_i2c_rd(MODULE_DEV_ADDR, 6); -// printf("SFP+ reg6 read as %x\n",x); +// UHD_FW_TRACE(DEBUG, "SFP+ reg6 read as %x",x); // if (x < 0) // return x; // if (x != 0x04) // Returns 1000Base-CX as Compliance code // goto unknown; x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0xA); if (x < 0) { - printf("DEBUG: I2C error in SFPP_TYPE.\n"); + UHD_FW_TRACE(ERROR, "I2C error in SFPP_TYPE."); return x; } if (x & 0x80) { @@ -292,117 +292,124 @@ xge_read_sfpp_type(const uint32_t base, const uint32_t delay_ms) x = xge_i2c_rd(base, MODULE_DEV_ADDR, 0x12); if (x < 0) { - printf("DEBUG: I2C error in SFPP_TYPE.\n"); + UHD_FW_TRACE(ERROR, "I2C error in SFPP_TYPE."); return x; } - printf("DEBUG: TwinAx.\n"); + UHD_FW_TRACE(DEBUG, "TwinAx."); // If cable length support is greater than 10M then pick correct type return x > 10 ? SFFP_TYPE_TWINAX_LONG : SFFP_TYPE_TWINAX; } unknown: - printf("DEBUG: Unknown SFP+ type.\n"); + UHD_FW_TRACE(WARN, "Unknown SFP+ type."); // Not a supported Module type return SFFP_TYPE_UNKNOWN; } - -// Pull reset line low for 100ms then release and wait 100ms -static void -xge_hard_phy_reset(const uint32_t base) +static void xge_mac_init(const uint32_t base) { - wb_poke32(base, 1); - mdelay(100); - wb_poke32(base, 0); - mdelay(100); - + UHD_FW_TRACE(DEBUG, "Begining XGE MAC init sequence."); + xge_regs->config = XGE_TX_ENABLE; } -static void -xge_mac_init(const uint32_t base) +// base is pointer to XGE MAC on Wishbone. +static void xge_phy_init(const uint8_t eth, const uint32_t mdio_port_arg) { - printf("INFO: Begining XGE MAC init sequence.\n"); - xge_regs->config = XGE_TX_ENABLE; + int x; + uint32_t mdio_port = eth==0 ? 1 : mdio_port_arg; + // Read LASI Ctrl register to capture state. + //y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A); + UHD_FW_TRACE(DEBUG, "Begining XGE PHY init sequence."); + // Software reset + x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port); + x = x | (1 << 15); + write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x); + while(x&(1<<15)) { + x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port); + } } -// base is pointer to XGE MAC on Wishbone. -static void -xge_phy_init(const uint8_t eth, const uint32_t mdio_port) +void update_eth_state(const uint32_t eth) { - int x; - // Read LASI Ctrl register to capture state. - //y = xge_read_mdio(0x9002,XGE_MDIO_DEVICE_PMA,XGE_MDIO_ADDR_PHY_A); - printf("INFO: Begining XGE PHY init sequence.\n"); - // Software reset - x = read_mdio(eth, 0x0, XGE_MDIO_DEVICE_PMA,mdio_port); - x = x | (1 << 15); - write_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port,x); - while(x&(1<<15)) - x = read_mdio(eth, 0x0,XGE_MDIO_DEVICE_PMA,mdio_port); + const bool old_link_up = links_up[eth]; + const uint32_t status_reg_addr = (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1; + const bool is_10g = (wb_peek32(SR_ADDR(RB0_BASE, eth == 0 ? RB_ETH_TYPE0 : RB_ETH_TYPE1)) == 1); + + uint32_t sfpp_status = wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) & 0xFFFF; + if ((sfpp_status & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) { + //SFP+ pin state changed. Reinitialize PHY and MAC + if (is_10g) { + xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE); + xge_phy_init(eth ,MDIO_PORT); + } else { + //No-op for 1G + } + + int8_t timeout = 100; + bool link_up = false; + do { + if (is_10g) { + link_up = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0; + } else { + link_up = ((wb_peek32(SR_ADDR(RB0_BASE, status_reg_addr)) >> 16) & 0x1) != 0; + } + } while (!link_up && timeout-- > 0); + + links_up[eth] = link_up; + } + else + { + links_up[eth] = false; + } + + if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth)); + UHD_FW_TRACE_FSTR(INFO, "The link on eth port %u is %s", eth, links_up[eth]?"up":"down"); } -void -xge_poll_sfpp_status(const uint32_t eth) +void poll_sfpp_status(const uint32_t eth) { - uint32_t x; - // Has MODDET/MODAbS changed since we last looked? - x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); - - if (x & SFPP_STATUS_RXLOS_CHG) - printf("DEBUG: eth%1d RXLOS changed state: %d\n", eth, x & SFPP_STATUS_RXLOS); - if (x & SFPP_STATUS_TXFAULT_CHG) - printf("DEBUG: eth%1d TXFAULT changed state: %d\n", eth,(x & SFPP_STATUS_TXFAULT) >> 1 ); - if (x & SFPP_STATUS_MODABS_CHG) - printf("DEBUG: eth%1d MODABS changed state: %d\n", eth, (x & SFPP_STATUS_MODABS) >> 2); - - if (x & (SFPP_STATUS_RXLOS_CHG|SFPP_STATUS_TXFAULT_CHG|SFPP_STATUS_MODABS_CHG)) - { - if (( x & (SFPP_STATUS_RXLOS|SFPP_STATUS_TXFAULT|SFPP_STATUS_MODABS)) == 0) + uint32_t x; + // Has MODDET/MODAbS changed since we last looked? + x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); + + if (x & SFPP_STATUS_RXLOS_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d RXLOS changed state: %d", eth, (x & SFPP_STATUS_RXLOS)); + if (x & SFPP_STATUS_TXFAULT_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d TXFAULT changed state: %d", eth, ((x & SFPP_STATUS_TXFAULT) >> 1)); + if (x & SFPP_STATUS_MODABS_CHG) + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d MODABS changed state: %d", eth, ((x & SFPP_STATUS_MODABS) >> 2)); + + //update the link up status + if ((x & SFPP_STATUS_RXLOS_CHG) || (x & SFPP_STATUS_TXFAULT_CHG) || (x & SFPP_STATUS_MODABS_CHG)) { - if (wb_peek32(SR_ADDR(RB0_BASE, eth == 0 ? RB_ETH_TYPE0 : RB_ETH_TYPE1)) == 1) - { - xge_ethernet_init(eth); - dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT); - mdelay(100); - dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT); - mdelay(100); - dump_mdio_regs((eth==0) ? XGE0_BASE : XGE1_BASE,MDIO_PORT); - } + update_eth_state(eth); } - } - if (x & SFPP_STATUS_MODABS_CHG) { - // MODDET has changed state since last checked - if (x & SFPP_STATUS_MODABS) { - // MODDET is high, module currently removed. - printf("INFO: An SFP+ module has been removed from eth port %d.\n", eth); - } else { - // MODDET is low, module currently inserted. - // Return status. - printf("INFO: A new SFP+ module has been inserted into eth port %d.\n", eth); - xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1); + if (x & SFPP_STATUS_MODABS_CHG) { + // MODDET has changed state since last checked + if (x & SFPP_STATUS_MODABS) { + // MODDET is high, module currently removed. + UHD_FW_TRACE_FSTR(INFO, "An SFP+ module has been removed from eth port %d.", eth); + } else { + // MODDET is low, module currently inserted. + // Return status. + UHD_FW_TRACE_FSTR(INFO, "A new SFP+ module has been inserted into eth port %d.", eth); + xge_read_sfpp_type((eth==0) ? I2C0_BASE : I2C2_BASE,1); + } } - } - - //update the link up status - const bool old_link_up = links_up[eth]; - links_up[eth] = ((read_mdio(eth, XGE_MDIO_STATUS1,XGE_MDIO_DEVICE_PMA,MDIO_PORT)) & (1 << 2)) != 0; - //The link became up, send a GARP so everyone knows our mac/ip association - if (!old_link_up && links_up[eth]) u3_net_stack_send_arp_request(eth, u3_net_stack_get_ip_addr(eth)); } - -void -xge_ethernet_init(const uint32_t eth) +void ethernet_init(const uint32_t eth) { - xge_mac_init((eth==0) ? XGE0_BASE : XGE1_BASE); - //xge_hard_phy_reset(); - xge_phy_init(eth ,MDIO_PORT); - uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); - printf(" eth%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d\n", - eth, - x & SFPP_STATUS_RXLOS, - (x & SFPP_STATUS_TXFAULT) >> 1, - (x & SFPP_STATUS_MODABS) >> 2); +#ifdef UHD_FW_TRACE_LEVEL + uint32_t x = wb_peek32(SR_ADDR(RB0_BASE, (eth==0) ? RB_SFPP_STATUS0 : RB_SFPP_STATUS1 )); + UHD_FW_TRACE_FSTR(DEBUG, "eth%1d SFP initial state: RXLOS: %d TXFAULT: %d MODABS: %d", + eth, + (x & SFPP_STATUS_RXLOS), + ((x & SFPP_STATUS_TXFAULT) >> 1), + ((x & SFPP_STATUS_MODABS) >> 2)); +#endif + links_up[eth] = false; + update_eth_state(eth); } // @@ -412,187 +419,179 @@ xge_ethernet_init(const uint32_t eth) void decode_reg(uint32_t address, uint32_t device, uint32_t data) { + UHD_FW_TRACE_FSTR(DEBUG, + "[MDIO Register Dump for Addr=%x, Device=%x]\n- Raw Value = %x", + address, device, data); int x; - printf("Device: "); - printf("%x",device); - printf(" "); switch(address) { case XGE_MDIO_CONTROL1: - printf("CONTROL1: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "CONTROL1: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 15: printf("Reset,"); break; - case 14: printf("Loopback,"); break; - case 11: printf("Low Power Mode,"); break; - case 5:case 4:case 3:case 2: printf("RESERVED speed value,"); break; - case 0: printf("PMA loopback,"); break; + case 15: UHD_FW_TRACE_SHORT(DEBUG, "Reset,"); break; + case 14: UHD_FW_TRACE_SHORT(DEBUG, "Loopback,"); break; + case 11: UHD_FW_TRACE_SHORT(DEBUG, "Low Power Mode,"); break; + case 5:case 4:case 3:case 2: UHD_FW_TRACE_SHORT(DEBUG, "RESERVED speed value,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "PMA loopback,"); break; } //else // Bits clear. //switch (x) { - //case 13: case 6: printf(" None 10Gb/s speed set!"); break; + //case 13: case 6: UHD_FW_TRACE_SHORT(DEBUG, " None 10Gb/s speed set!"); break; //} - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_STATUS1: - printf("STATUS1: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "STATUS1: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 7: printf("Fault Detected,"); break; - case 2: printf("Link is Up,"); break; - case 1: printf("Supports Low Power,"); break; + case 7: UHD_FW_TRACE_SHORT(DEBUG, "Fault Detected,"); break; + case 2: UHD_FW_TRACE_SHORT(DEBUG, "Link is Up,"); break; + case 1: UHD_FW_TRACE_SHORT(DEBUG, "Supports Low Power,"); break; } else // Bits Clear switch(x) { - case 2: printf("Link is Down,"); break; + case 2: UHD_FW_TRACE_SHORT(DEBUG, "Link is Down,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_SPEED: - printf("SPEED ABILITY: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "SPEED ABILITY: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { case 15:case 14:case 13:case 12:case 11:case 10:case 9: - case 8:case 7:case 6:case 5:case 4:case 3:case 2:case 1: printf("RESERVED bits set!,"); break; - case 0: printf("Capable of 10Gb/s,"); + case 8:case 7:case 6:case 5:case 4:case 3:case 2:case 1: UHD_FW_TRACE_SHORT(DEBUG, "RESERVED bits set!,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "Capable of 10Gb/s,"); } else // Bits clear. switch(x) { - case 0: printf("Incapable of 10Gb/s,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "Incapable of 10Gb/s,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_DEVICES1: - printf("DEVICES IN PACKAGE: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "DEVICES IN PACKAGE: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 7: printf("Auto-Negotiation,"); break; - case 6: printf("TC,"); break; - case 5: printf("DTE XS,"); break; - case 4: printf("PHY XS,"); break; - case 3: printf("PCS,"); break; - case 2: printf("WIS,"); break; - case 1: printf("PMD/PMA,"); break; - case 0: printf("Clause 22 registers,"); break; + case 7: UHD_FW_TRACE_SHORT(DEBUG, "Auto-Negotiation,"); break; + case 6: UHD_FW_TRACE_SHORT(DEBUG, "TC,"); break; + case 5: UHD_FW_TRACE_SHORT(DEBUG, "DTE XS,"); break; + case 4: UHD_FW_TRACE_SHORT(DEBUG, "PHY XS,"); break; + case 3: UHD_FW_TRACE_SHORT(DEBUG, "PCS,"); break; + case 2: UHD_FW_TRACE_SHORT(DEBUG, "WIS,"); break; + case 1: UHD_FW_TRACE_SHORT(DEBUG, "PMD/PMA,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "Clause 22 registers,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_DEVICES2: - printf("DEVICES IN PACKAGE (cont): "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "DEVICES IN PACKAGE (cont): %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 15: printf("Vendor device 2,"); break; - case 14: printf("Vendor device 1,"); break; - case 13: printf("Clause 22 extension,"); break; + case 15: UHD_FW_TRACE_SHORT(DEBUG, "Vendor device 2,"); break; + case 14: UHD_FW_TRACE_SHORT(DEBUG, "Vendor device 1,"); break; + case 13: UHD_FW_TRACE_SHORT(DEBUG, "Clause 22 extension,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_CONTROL2: - printf("CONTROL2: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "CONTROL2: %x = ", data); // PMA/PMD if (device == XGE_MDIO_DEVICE_PMA) switch((data & 0xf)) { - case 0xF: printf("10BASE-T,"); break; - case 0xE: printf("100BASE-TX,"); break; - case 0xD: printf("1000BASE-KX,"); break; - case 0xC: printf("1000BASE-T,"); break; - case 0xB: printf("10GBASE-KR,"); break; - case 0xA: printf("10GBASE-KX4,"); break; - case 0x9: printf("10GBASE-T,"); break; - case 0x8: printf("10GBASE-LRM,"); break; - case 0x7: printf("10GBASE-SR,"); break; - case 0x6: printf("10GBASE-LR,"); break; - case 0x5: printf("10GBASE-ER,"); break; - case 0x4: printf("10GBASE-LX4,"); break; - // case 0x3: printf("10GBASE-SW,"); break; - // case 0x2: printf("10GBASE-LW,"); break; - // case 0x1: printf("10GBASE-EW,"); break; - case 0x0: printf("10GBASE-CX4,"); break; + case 0xF: UHD_FW_TRACE_SHORT(DEBUG, "10BASE-T,"); break; + case 0xE: UHD_FW_TRACE_SHORT(DEBUG, "100BASE-TX,"); break; + case 0xD: UHD_FW_TRACE_SHORT(DEBUG, "1000BASE-KX,"); break; + case 0xC: UHD_FW_TRACE_SHORT(DEBUG, "1000BASE-T,"); break; + case 0xB: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-KR,"); break; + case 0xA: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-KX4,"); break; + case 0x9: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-T,"); break; + case 0x8: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LRM,"); break; + case 0x7: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-SR,"); break; + case 0x6: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LR,"); break; + case 0x5: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-ER,"); break; + case 0x4: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LX4,"); break; + // case 0x3: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-SW,"); break; + // case 0x2: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LW,"); break; + // case 0x1: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-EW,"); break; + case 0x0: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-CX4,"); break; } else if (device == XGE_MDIO_DEVICE_PCS) // PCS switch((data & 0x3)) { - case 0x3: printf("10GBASE-T PCS,"); break; - case 0x2: printf("10GBASE-W PCS,"); break; - case 0x1: printf("10GBASE-X PCS,"); break; - case 0x0: printf("10GBASE-R PCS,"); break; + case 0x3: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-T PCS,"); break; + case 0x2: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-W PCS,"); break; + case 0x1: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-X PCS,"); break; + case 0x0: UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-R PCS,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_STATUS2: - printf("STATUS2: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "STATUS2: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 15: if ((data & (1 << 14)) == 0) printf("Device responding,"); break; - case 13: if (device == XGE_MDIO_DEVICE_PMA) printf("Able detect a Tx fault,"); break; - case 12: if (device == XGE_MDIO_DEVICE_PMA) printf("Able detect an Rx fault,"); break; - case 11: printf("Fault on Tx path,"); break; - case 10: printf("Fault on Rx path,"); break; - case 9: if (device == XGE_MDIO_DEVICE_PMA) printf("Extended abilities in Reg1.11,"); break; - case 8: if (device == XGE_MDIO_DEVICE_PMA) printf("Able to disable TX,"); break; - case 7: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-SR,"); break; - case 6: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LR,"); break; - case 5: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-ER,"); break; - case 4: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LX4,"); break; - case 3: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-SW,"); break; - case 2: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-LW,"); break; - case 1: if (device == XGE_MDIO_DEVICE_PMA) printf("10GBASE-EW,"); break; - case 0: if (device == XGE_MDIO_DEVICE_PMA) printf("loopback,"); break; + case 15: if ((data & (1 << 14)) == 0) UHD_FW_TRACE_SHORT(DEBUG, "Device responding,"); break; + case 13: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "Able detect a Tx fault,"); break; + case 12: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "Able detect an Rx fault,"); break; + case 11: UHD_FW_TRACE_SHORT(DEBUG, "Fault on Tx path,"); break; + case 10: UHD_FW_TRACE_SHORT(DEBUG, "Fault on Rx path,"); break; + case 9: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "Extended abilities in Reg1.11,"); break; + case 8: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "Able to disable TX,"); break; + case 7: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-SR,"); break; + case 6: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LR,"); break; + case 5: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-ER,"); break; + case 4: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LX4,"); break; + case 3: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-SW,"); break; + case 2: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-LW,"); break; + case 1: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "10GBASE-EW,"); break; + case 0: if (device == XGE_MDIO_DEVICE_PMA) UHD_FW_TRACE_SHORT(DEBUG, "loopback,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XGE_MDIO_LANESTATUS: - printf("LANE STATUS: "); - printf("%x",data); printf(" "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "LANE STATUS: %x = ", data); for (x=15; x >= 0 ; x--) if ((data & (1 << x)) != 0) // Bits set. switch(x) { - case 12: printf("Lanes aligned,"); break; - case 11: printf("Able to generate test patterns,"); break; - case 3: printf("Lane 3 synced,"); break; - case 2: printf("Lane 2 synced,"); break; - case 1: printf("Lane 1 synced,"); break; - case 0: printf("Lane 0 synced,"); break; + case 12: UHD_FW_TRACE_SHORT(DEBUG, "Lanes aligned,"); break; + case 11: UHD_FW_TRACE_SHORT(DEBUG, "Able to generate test patterns,"); break; + case 3: UHD_FW_TRACE_SHORT(DEBUG, "Lane 3 synced,"); break; + case 2: UHD_FW_TRACE_SHORT(DEBUG, "Lane 2 synced,"); break; + case 1: UHD_FW_TRACE_SHORT(DEBUG, "Lane 1 synced,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "Lane 0 synced,"); break; } else // Bits clear switch(x) { - case 3: printf("Lane 3 not synced,"); break; - case 2: printf("Lane 2 not synced,"); break; - case 1: printf("Lane 1 not synced,"); break; - case 0: printf("Lane 0 not synced,"); break; + case 3: UHD_FW_TRACE_SHORT(DEBUG, "Lane 3 not synced,"); break; + case 2: UHD_FW_TRACE_SHORT(DEBUG, "Lane 2 not synced,"); break; + case 1: UHD_FW_TRACE_SHORT(DEBUG, "Lane 1 not synced,"); break; + case 0: UHD_FW_TRACE_SHORT(DEBUG, "Lane 0 not synced,"); break; } - printf(" \n"); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); break; case XILINX_CORE_VERSION: - printf("XILINX CORE VERSION: %x ",data); - printf("Version: %d.%d ",(data&0xf000)>>12,(data&0xf00)>>8); - printf("Patch: %d ",(data&0xE)>>1); - if (data&0x1) printf("Evaluation Version of core"); - printf("\n"); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "XILINX CORE VERSION: %x ",data); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "Version: %d.%d ",((data&0xf000)>>12),((data&0xf00)>>8)); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "Patch: %d ",((data&0xE)>>1)); + UHD_FW_TRACE_SHORT(DEBUG, " \n"); + if (data&0x1) UHD_FW_TRACE(WARN, "Evaluation Version of core"); break; default: - printf("Register @ address: "); - printf("%x",address); - printf(" has value: "); - printf("%x\n",data); + UHD_FW_TRACE_SHORT(DEBUG, "Register @ address: "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "%x",address); + UHD_FW_TRACE_SHORT(DEBUG, " has value: "); + UHD_FW_TRACE_FSTR_SHORT(DEBUG, "%x\n",data); break; } } @@ -605,7 +604,6 @@ dump_mdio_regs(const uint8_t eth, uint32_t mdio_port) unsigned int regs_a[9] = {0,1,4,5,6,7,8,32,33}; unsigned int regs_b[10] = {0,1,4,5,6,7,8,10,11,65535}; - printf("\n"); for (y = 0; y < 10; y++) { @@ -621,7 +619,6 @@ dump_mdio_regs(const uint8_t eth, uint32_t mdio_port) decode_reg(regs_a[y],XGE_MDIO_DEVICE_PCS,x); } - printf("\n"); /* for (y = 0; y < 8; y++) */ /* { */ diff --git a/firmware/usrp3/lib/u3_net_stack.c b/firmware/usrp3/lib/u3_net_stack.c index 6b8ef096c..16eb0f9fe 100644 --- a/firmware/usrp3/lib/u3_net_stack.c +++ b/firmware/usrp3/lib/u3_net_stack.c @@ -3,7 +3,7 @@ #include <u3_net_stack.h> #include <string.h> //memcmp -#include <printf.h> +#include <trace.h> #define MAX_NETHS 4 @@ -281,7 +281,7 @@ void u3_net_stack_send_arp_request(const uint8_t ethno, const struct ip_addr *ad static void handle_arp_packet(const uint8_t ethno, const struct arp_eth_ipv4 *p) { - //printf("handle_arp_packet\n"); + UHD_FW_TRACE(DEBUG, "handle_arp_packet"); if (p->ar_hrd != ARPHRD_ETHER || p->ar_pro != ETHERTYPE_IPV4 || p->ar_hln != sizeof(eth_mac_addr_t) @@ -291,7 +291,7 @@ static void handle_arp_packet(const uint8_t ethno, const struct arp_eth_ipv4 *p) //got an arp reply -- injest it into the arp cache if (p->ar_op == ARPOP_REPLY) { - //printf("ARPOP_REPLY\n"); + UHD_FW_TRACE(DEBUG, "ARPOP_REPLY"); struct ip_addr ip_addr; memcpy(&ip_addr, p->ar_sip, sizeof(ip_addr)); eth_mac_addr_t mac_addr; @@ -302,7 +302,7 @@ static void handle_arp_packet(const uint8_t ethno, const struct arp_eth_ipv4 *p) //got an arp request -- reply if its for our address if (p->ar_op == ARPOP_REQUEST) { - //printf("ARPOP_REQUEST\n"); + UHD_FW_TRACE(DEBUG, "ARPOP_REQUEST"); if (memcmp(p->ar_tip, u3_net_stack_get_ip_addr(ethno), sizeof(struct ip_addr)) == 0) { send_arp_reply(ethno, p, u3_net_stack_get_mac_addr(ethno)); @@ -344,7 +344,7 @@ void u3_net_stack_send_udp_pkt( eth_mac_addr_t dst_mac_addr; if (!resolve_ip(dst, &dst_mac_addr)) { - printf("u3_net_stack_send_udp_pkt arp_cache_lookup fail\n"); + UHD_FW_TRACE(WARN, "u3_net_stack_send_udp_pkt arp_cache_lookup fail"); return; } @@ -396,7 +396,7 @@ static void handle_udp_packet( return; } } - printf("Unhandled UDP packet src=%u, dest=%u\n", udp->src, udp->dest); + UHD_FW_TRACE_FSTR(ERROR, "Unhandled UDP packet src=%u, dest=%u", udp->src, udp->dest); //TODO send destination unreachable } @@ -445,7 +445,7 @@ static void handle_icmp_packet( return; } } - printf("Unhandled ICMP packet type=%u\n", icmp->type); + UHD_FW_TRACE_FSTR(ERROR, "Unhandled ICMP packet type=%u", icmp->type); } static void handle_icmp_dur_packet( @@ -484,7 +484,7 @@ void u3_net_stack_send_icmp_pkt( eth_mac_addr_t dst_mac_addr; if (!resolve_ip(dst, &dst_mac_addr)) { - printf("u3_net_stack_send_echo_request arp_cache_lookup fail\n"); + UHD_FW_TRACE(WARN, "u3_net_stack_send_echo_request arp_cache_lookup fail"); return; } @@ -533,17 +533,17 @@ static void handle_eth_packet(const void *buff, const size_t num_bytes) { const padded_eth_hdr_t *eth_hdr = (padded_eth_hdr_t *)buff; const uint8_t *eth_body = ((const uint8_t *)buff) + sizeof(padded_eth_hdr_t); - //printf("handle_eth_packet got ethertype 0x%x\n", (unsigned)eth_hdr->ethertype); + UHD_FW_TRACE_FSTR(DEBUG, "handle_eth_packet got ethertype 0x%x", (unsigned)eth_hdr->ethertype); if (eth_hdr->ethertype == ETHERTYPE_ARP) { - //printf("eth_hdr->ethertype == ETHERTYPE_ARP\n"); + UHD_FW_TRACE(DEBUG, "eth_hdr->ethertype == ETHERTYPE_ARP"); const struct arp_eth_ipv4 *arp = (const struct arp_eth_ipv4 *)eth_body; handle_arp_packet(eth_hdr->ethno, arp); } else if (eth_hdr->ethertype == ETHERTYPE_IPV4) { - //printf("eth_hdr->ethertype == ETHERTYPE_IPV4\n"); + UHD_FW_TRACE(DEBUG, "eth_hdr->ethertype == ETHERTYPE_IPV4"); const struct ip_hdr *ip = (const struct ip_hdr *)eth_body; const uint8_t *ip_body = eth_body + IP_HLEN; @@ -572,7 +572,7 @@ static void handle_eth_packet(const void *buff, const size_t num_bytes) ); } } - else return; // Not ARP or IPV4, ignore + else return; // Not ARP or IPV4, ignore } void u3_net_stack_handle_one(void) @@ -581,7 +581,7 @@ void u3_net_stack_handle_one(void) const void *ptr = wb_pkt_iface64_rx_try_claim(pkt_iface_config, &num_bytes); if (ptr != NULL) { - //printf("u3_net_stack_handle_one got %u bytes\n", (unsigned)num_bytes); + UHD_FW_TRACE_FSTR(DEBUG, "u3_net_stack_handle_one got %u bytes", (unsigned)num_bytes); incr_stat_counts(ptr); handle_eth_packet(ptr, num_bytes); wb_pkt_iface64_rx_release(pkt_iface_config); diff --git a/firmware/usrp3/x300/x300_defs.h b/firmware/usrp3/x300/x300_defs.h index 65c5d5a23..c4011bd12 100644 --- a/firmware/usrp3/x300/x300_defs.h +++ b/firmware/usrp3/x300/x300_defs.h @@ -51,6 +51,7 @@ static const int RB_SPI_RDY = 1; static const int RB_SPI_DATA = 2; static const int RB_ETH_TYPE0 = 4; static const int RB_ETH_TYPE1 = 5; +static const int RB_FPGA_COMPAT = 6; static const int RB_SFPP_STATUS0 = 8; static const int RB_SFPP_STATUS1 = 9; diff --git a/firmware/usrp3/x300/x300_init.c b/firmware/usrp3/x300/x300_init.c index 66fb120f3..ef97412a2 100644 --- a/firmware/usrp3/x300/x300_init.c +++ b/firmware/usrp3/x300/x300_init.c @@ -7,7 +7,7 @@ #include <wb_i2c.h> #include <stdint.h> #include <stdbool.h> -#include <printf.h> +#include <trace.h> #include <wb_pkt_iface64.h> #include <u3_net_stack.h> #include <link_state_route_proto.h> @@ -73,7 +73,6 @@ const void *pick_inited_field(const void *eeprom, const void *def, const size_t static void init_network(void) { pkt_config = wb_pkt_iface64_init(PKT_RAM0_BASE, 0x1ffc); - printf("PKT RAM0 BASE 0x%x\n", (&pkt_config)->base); u3_net_stack_init(&pkt_config); link_state_route_proto_init(); @@ -115,7 +114,9 @@ static void init_network(void) static void putc(void *p, char c) { -#ifdef X300_DEBUG_UART +//If FW_TRACE_LEVEL is defined, then the trace level is set +//to a non-zero number. Turn on the debug UART to enable tracing +#ifdef UHD_FW_TRACE_LEVEL wb_uart_putc(UART1_BASE, c); #endif } @@ -129,7 +130,11 @@ void x300_init(void) //udp_uart_init(UART0_BASE, X300_GPSDO_UDP_PORT); //now we can init the rest with prints - printf("X300 ZPU Init Begin -- CPU CLOCK is %d MHz\n", CPU_CLOCK/1000000); + UHD_FW_TRACE(INFO, "[ZPU Initializing]"); + UHD_FW_TRACE_FSTR(INFO, "-- Firmware Compat Number: %u.%u", (int)X300_FW_COMPAT_MAJOR, (int)X300_FW_COMPAT_MINOR); + uint32_t fpga_compat = wb_peek32(SR_ADDR(SET0_BASE, RB_FPGA_COMPAT)); + UHD_FW_TRACE_FSTR(INFO, "-- FPGA Compat Number: %u.%u", (fpga_compat>>16), (fpga_compat&0xFFFF)); + UHD_FW_TRACE_FSTR(INFO, "-- Clock Frequency: %u MHz", (CPU_CLOCK/1000000)); //i2c rate init wb_i2c_init(I2C0_BASE, CPU_CLOCK); @@ -139,30 +144,26 @@ void x300_init(void) //hold phy in reset wb_poke32(SR_ADDR(SET0_BASE, SR_SW_RST), SW_RST_PHY); - printf("DEBUG: eth0 is %2dG\n",(wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE0))==1) ? 10 : 1); - printf("DEBUG: eth1 is %2dG\n",(wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE1))==1) ? 10 : 1); - //setup net stack and eth state machines init_network(); //phy reset release wb_poke32(SR_ADDR(SET0_BASE, SR_SW_RST), 0); - // For eth interfaces, initialize the PHY's - mdelay(100); - if (wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE0)) == 1) { - xge_ethernet_init(0); - } - if (wb_peek32(SR_ADDR(RB0_BASE, RB_ETH_TYPE1)) == 1) { - xge_ethernet_init(1); - } - //print network summary for (uint8_t e = 0; e < ethernet_ninterfaces(); e++) { - printf(" MAC%u: %s\n", (int)e, mac_addr_to_str(u3_net_stack_get_mac_addr(e))); - printf(" IP%u: %s\n", (int)e, ip_addr_to_str(u3_net_stack_get_ip_addr(e))); - printf(" SUBNET%u: %s\n", (int)e, ip_addr_to_str(u3_net_stack_get_subnet(e))); - printf(" BCAST%u: %s\n", (int)e, ip_addr_to_str(u3_net_stack_get_bcast(e))); + uint32_t offset = SR_ADDR(RB0_BASE, ((e==1)?RB_ETH_TYPE1:RB_ETH_TYPE0)); + UHD_FW_TRACE_FSTR(INFO, "Ethernet Port %u:", (int)e); + UHD_FW_TRACE_FSTR(INFO, "-- PHY: %s", ((wb_peek32(offset)==1) ? "10Gbps" : "1Gbps")); + UHD_FW_TRACE_FSTR(INFO, "-- MAC: %s", mac_addr_to_str(u3_net_stack_get_mac_addr(e))); + UHD_FW_TRACE_FSTR(INFO, "-- IP: %s", ip_addr_to_str(u3_net_stack_get_ip_addr(e))); + UHD_FW_TRACE_FSTR(INFO, "-- SUBNET: %s", ip_addr_to_str(u3_net_stack_get_subnet(e))); + UHD_FW_TRACE_FSTR(INFO, "-- BCAST: %s", ip_addr_to_str(u3_net_stack_get_bcast(e))); } + + // For eth interfaces, initialize the PHY's + mdelay(100); + ethernet_init(0); + ethernet_init(1); } diff --git a/firmware/usrp3/x300/x300_main.c b/firmware/usrp3/x300/x300_main.c index d865e1d09..3b812a2c4 100644 --- a/firmware/usrp3/x300/x300_main.c +++ b/firmware/usrp3/x300/x300_main.c @@ -13,7 +13,7 @@ #include <udp_uart.h> #include <u3_net_stack.h> #include <link_state_route_proto.h> -#include <printf.h> +#include <trace.h> #include <string.h> #include <print_addrs.h> @@ -33,7 +33,7 @@ void program_udp_framer( const eth_mac_addr_t *dst_mac = u3_net_stack_arp_cache_lookup(dst_ip); const size_t ethbase = (ethno == 0)? SR_ETHINT0 : SR_ETHINT1; const size_t vdest = (sid >> 16) & 0xff; - printf("handle_udp_prog_framer sid %u vdest %u\n", sid, vdest); + UHD_FW_TRACE_FSTR(INFO, "handle_udp_prog_framer sid %u vdest %u\n", sid, vdest); //setup source framer const eth_mac_addr_t *src_mac = u3_net_stack_get_mac_addr(ethno); @@ -204,7 +204,7 @@ void handle_udp_mtu_detect( if (buff == NULL) { return; } else if (!(request->flags & X300_MTU_DETECT_ECHO_REQUEST)) { - printf("DEBUG: MTU detect got unknown request\n"); + UHD_FW_TRACE(WARN, "MTU detect got unknown request"); reply.flags |= X300_MTU_DETECT_ERROR; } @@ -445,12 +445,12 @@ int main(void) static const uint32_t tick_delta = CPU_CLOCK/1000; if (ticks_passed > tick_delta) { + poll_sfpp_status(0); // Every so often poll XGE Phy to look for SFP+ hotplug events. + poll_sfpp_status(1); // Every so often poll XGE Phy to look for SFP+ hotplug events. handle_link_state(); //deal with router table update handle_claim(); //deal with the host claim register update_leds(); //run the link and activity leds garp(); //send periodic garps - xge_poll_sfpp_status(0); // Every so often poll XGE Phy to look for SFP+ hotplug events. - xge_poll_sfpp_status(1); // Every so often poll XGE Phy to look for SFP+ hotplug events. last_cronjob = wb_peek32(SR_ADDR(RB0_BASE, RB_COUNTER)); } diff --git a/fpga-src b/fpga-src -Subproject dd8147301da1dafe9cc985227317bc3cd12c528 +Subproject da37ebb6f909f3813617f346d500506244aa5a1 diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index a4c498c2b..3dd563592 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -23,7 +23,7 @@ ######################################################################## # Project setup ######################################################################## -CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(UHD CXX C) ENABLE_TESTING() @@ -164,6 +164,7 @@ IF(MSVC) -D_CRT_NONSTDC_NO_DEPRECATE ) ADD_DEFINITIONS(/MP) #multi-threaded build + ADD_DEFINITIONS(/bigobj) #Increases the number of addressable sections in an .obj file. ENDIF(MSVC) IF(CYGWIN) @@ -235,9 +236,9 @@ PYTHON_CHECK_MODULE( ) PYTHON_CHECK_MODULE( - "Cheetah templates 2.0.0 or greater" - "Cheetah" "Cheetah.Version >= '2.0.0'" - HAVE_PYTHON_MODULE_CHEETAH + "Mako templates 0.4 or greater" + "mako" "mako.__version__ >= '0.4.2'" + HAVE_PYTHON_MODULE_MAKO ) ######################################################################## @@ -268,24 +269,25 @@ UHD_INSTALL(FILES #{{{IMG_SECTION # This section is written automatically by /images/create_imgs_package.py # Any manual changes in here will be overwritten. -SET(UHD_IMAGES_MD5SUM "97323c3d2ccec9b729e02a44e10e5480") -SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.008.005-release.zip") +SET(UHD_IMAGES_MD5SUM "0df3492fda2bd52db47d9f9e6ae475ca") +SET(UHD_IMAGES_DOWNLOAD_SRC "uhd-images_003.009.000-release.zip") #}}} ######################################################################## # Register top level components ######################################################################## -LIBUHD_REGISTER_COMPONENT("LibUHD" ENABLE_LIBUHD ON "Boost_FOUND;HAVE_PYTHON_PLAT_MIN_VERSION;HAVE_PYTHON_MODULE_CHEETAH" OFF) -LIBUHD_REGISTER_COMPONENT("Examples" ENABLE_EXAMPLES ON "ENABLE_LIBUHD" OFF) -LIBUHD_REGISTER_COMPONENT("Utils" ENABLE_UTILS ON "ENABLE_LIBUHD" OFF) -LIBUHD_REGISTER_COMPONENT("Tests" ENABLE_TESTS ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("LibUHD" ENABLE_LIBUHD ON "Boost_FOUND;HAVE_PYTHON_PLAT_MIN_VERSION;HAVE_PYTHON_MODULE_MAKO" OFF ON) +LIBUHD_REGISTER_COMPONENT("LibUHD - C API" ENABLE_C_API ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("Examples" ENABLE_EXAMPLES ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("Utils" ENABLE_UTILS ON "ENABLE_LIBUHD" OFF OFF) +LIBUHD_REGISTER_COMPONENT("Tests" ENABLE_TESTS ON "ENABLE_LIBUHD" OFF OFF) ######################################################################## # Check for fpga-src submodule ######################################################################## SET(HAS_FPGA_SUBMODULE FALSE) EXECUTE_PROCESS( - COMMAND ${PYTHON_EXECUTABLE} -c "import os; print os.path.abspath(os.path.join('${CMAKE_SOURCE_DIR}', '..', 'fpga-src'))" + COMMAND ${PYTHON_EXECUTABLE} -c "import os; print(os.path.abspath(os.path.join('${CMAKE_SOURCE_DIR}', '..', 'fpga-src')))" OUTPUT_VARIABLE FPGA_SUBMODULE_DIR OUTPUT_STRIP_TRAILING_WHITESPACE ) @@ -369,9 +371,6 @@ ENDFOREACH(Boost_Comp) IF(ENABLE_USB) LIST(APPEND UHD_LINK_LIST_STATIC "usb-1.0") ENDIF(ENABLE_USB) -IF(ENABLE_ORC) - LIST(APPEND UHD_LINK_LIST_STATIC "orc-0.4") -ENDIF(ENABLE_ORC) CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/cmake/Modules/UHDConfigVersion.cmake.in @@ -421,5 +420,19 @@ ELSEIF(UHDHOST_PKG) SET(PRINT_APPEND " (Debian uhd-host package configuration)") ENDIF(LIBUHD_PKG) UHD_PRINT_COMPONENT_SUMMARY() +IF(UHD_VERSION_DEVEL) + MESSAGE(STATUS "******************************************************") + IF(UHD_VERSION_PATCH STREQUAL "git") + MESSAGE(STATUS "* You are building the UHD development master branch.") + MESSAGE(STATUS "* For production code, we recommend our stable,") + MESSAGE(STATUS "* releases or using the release branch (maint).") + ELSE() + MESSAGE(STATUS "* You are building a development branch of UHD.") + MESSAGE(STATUS "* These branches are designed to provide early access") + MESSAGE(STATUS "* to UHD and USRP features, but should be considered") + MESSAGE(STATUS "* unstable and/or experimental!") + ENDIF(UHD_VERSION_PATCH STREQUAL "git") + MESSAGE(STATUS "******************************************************") +ENDIF(UHD_VERSION_DEVEL) MESSAGE(STATUS "Building version: ${UHD_VERSION}${PRINT_APPEND}") MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") diff --git a/host/apps/omap_debug/.gitignore b/host/apps/omap_debug/.gitignore deleted file mode 100644 index 008a23138..000000000 --- a/host/apps/omap_debug/.gitignore +++ /dev/null @@ -1,20 +0,0 @@ -.gitignore -clkgen-config -fpga-downloader -usrp-e-button -usrp-e-crc-rw -usrp-e-ctl -usrp-e-debug-pins -usrp-e-fpga-rw -usrp-e-gpio -usrp-e-i2c -usrp-e-lb-test -usrp-e-led -usrp-e-loopback -usrp-e-random-loopback -usrp-e-rw -usrp-e-spi -usrp-e-timed -usrp-e-uart -usrp-e-uart-rx -usrp-e-mm-loopback diff --git a/host/apps/omap_debug/Makefile b/host/apps/omap_debug/Makefile deleted file mode 100644 index f8b9f2bd9..000000000 --- a/host/apps/omap_debug/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -CFLAGS=-Wall -I../../lib/usrp/usrp_e/ -march=armv7-a -mtune=cortex-a8 -mfpu=neon -O3 -CXXFLAGS=-Wall -I../../lib/usrp/usrp_e/ -march=armv7-a -mtune=cortex-a8 -mfpu=neon -O3 - -all : usrp-e-spi usrp-e-i2c usrp-e-uart usrp-e-led usrp-e-ctl usrp-e-button usrp-e-uart-rx usrp-e-gpio usrp-e-debug-pins - -usrp-e-spi : usrp-e-spi.c - -usrp-e-i2c : usrp-e-i2c.c - -usrp-e-uart : usrp-e-uart.c - -usrp-e-uart-rx : usrp-e-uart-rx.c - -usrp-e-led : usrp-e-led.c - -usrp-e-ctl : usrp-e-ctl.c - -usrp-e-button : usrp-e-button.c - -usrp-e-gpio : usrp-e-gpio.c - -usrp-e-debug-pins : usrp-e-debug-pins.c -clean : - rm -f usrp-e-spi - rm -f usrp-e-i2c - rm -f usrp-e-uart - rm -f usrp-e-uart-rx - rm -f usrp-e-led - rm -f usrp-e-ctl - rm -f usrp-e-button - rm -f usrp-e-gpio - rm -f usrp-e-debug-pins - rm -f usrp-e-lb-test diff --git a/host/apps/omap_debug/set_debug_pins.py b/host/apps/omap_debug/set_debug_pins.py deleted file mode 100755 index 0f9ecd7b9..000000000 --- a/host/apps/omap_debug/set_debug_pins.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/python - -import os - -# Memory Map -misc_base = 0 -uart_base = 1 -spi_base = 2 -i2c_base = 3 -gpio_base = 4 * 128 -settings_base = 5 - -# GPIO offset -gpio_pins = 0 -gpio_ddr = 4 -gpio_ctrl_lo = 8 -gpio_ctrl_hi = 12 - -def set_reg(reg, val): - os.system("./usrp1-e-ctl w %d 1 %d" % (reg,val)) - -def get_reg(reg): - fin,fout = os.popen4("./usrp1-e-ctl r %d 1" % (reg,)) - print fout.read() - -# Set DDRs to output -set_reg(gpio_base+gpio_ddr, 0xFFFF) -set_reg(gpio_base+gpio_ddr+2, 0xFFFF) - -# Set CTRL to Debug #0 ( A is for debug 0, F is for debug 1 ) -set_reg(gpio_base+gpio_ctrl_lo, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_lo+2, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_hi, 0xAAAA) -set_reg(gpio_base+gpio_ctrl_hi+2, 0xAAAA) - diff --git a/host/apps/omap_debug/test.c b/host/apps/omap_debug/test.c deleted file mode 100644 index 36f4d700a..000000000 --- a/host/apps/omap_debug/test.c +++ /dev/null @@ -1,34 +0,0 @@ -#include <stdio.h> - -void -main() -{ - int x; - char *y; - long long z; - - x = 0x01020304; - z = 0x0102030405060708LL; - - printf("%x\n",x); - y = (char *)&x; - printf("%x\n",y[0]); - printf("%x\n",y[1]); - printf("%x\n",y[2]); - printf("%x\n",y[3]); - - printf("Printing z ...\n"); - printf("%llx\n",z); - printf("Printing z done\n"); - - y = (char *)&z; - printf("%x\n",y[0]); - printf("%x\n",y[1]); - printf("%x\n",y[2]); - printf("%x\n",y[3]); - printf("%x\n",y[4]); - printf("%x\n",y[5]); - printf("%x\n",y[6]); - printf("%x\n",y[7]); -} - diff --git a/host/apps/omap_debug/u1e-read-stream.c b/host/apps/omap_debug/u1e-read-stream.c deleted file mode 100644 index 4e4c21d9e..000000000 --- a/host/apps/omap_debug/u1e-read-stream.c +++ /dev/null @@ -1,21 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, cnt, n; - short buf[1024]; - - n = 0; - - fp = open("/dev/usrp1_e0", O_RDONLY); - printf("fp = %d\n", fp); - - do { - cnt = read(fp, buf, 2048); - n++; -// printf("Bytes read - %d\n", cnt); - } while(n < 10*512); - printf("Data - %hX\n", buf[0]); -} diff --git a/host/apps/omap_debug/usrp-e-button.c b/host/apps/omap_debug/usrp-e-button.c deleted file mode 100644 index f13291491..000000000 --- a/host/apps/omap_debug/usrp-e-button.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - -#define PB1 (1<<8) -#define PB2 (1<<9) -#define PB3 (1<<10) -#define P1 (0) -#define P2 (0xFF) -#define P3 (0xAA) -#define P4 (0x55) - -int main(int argc, char *argv[]) -{ - int fp, ret; - struct usrp_e_ctl16 d; - int pb1=0, pb2=0, pb3=0, p1=0, p2=0, p3=0, p4=0; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - d.offset = UE_REG_MISC_SW; - d.count = 1; - - do { - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - if (d.buf[0] & PB1) { - pb1 = 1; - printf("Pushbutton 1 hit\n"); - } - - if (d.buf[0] & PB2) { - pb2 = 1; - printf("Pushbutton 2 hit\n"); - } - - if (d.buf[0] & PB3) { - pb3 = 1; - printf("Pushbutton 3 hit\n"); - } - - sleep(1); - - } while (!(pb1 && pb2 && pb3)); - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-ctl.c b/host/apps/omap_debug/usrp-e-ctl.c deleted file mode 100644 index 69c48ee6f..000000000 --- a/host/apps/omap_debug/usrp-e-ctl.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_ctl w|r offset number_of_values val1 val2 .... - -int main(int argc, char *argv[]) -{ - int fp, i, cnt, ret; - struct usrp_e_ctl16 ctl_data; - - if (argc < 4) { - printf("Usage: usrp_e_ctl w|r offset number_of_values val1 val2 ....\n"); - exit(-1); - } - - cnt = atoi(argv[3]); - - ctl_data.offset = atoi(argv[2]); - ctl_data.count = cnt; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (*argv[1] == 'w') { - for (i=0; i<cnt; i++) - ctl_data.buf[i] = atoi(argv[4+i]); - - ret = ioctl(fp, USRP_E_WRITE_CTL16, &ctl_data); - printf("Return value from write ioctl = %d\n", ret); - } - - if (*argv[1] == 'r') { - ret = ioctl(fp, USRP_E_READ_CTL16, &ctl_data); - printf("Return value from write ioctl = %d\n", ret); - - for (i=0; i<ctl_data.count; i++) { - if (!(i%8)) - printf("\nData at %4d :", i); - printf(" %5d", ctl_data.buf[i]); - } - printf("\n"); - } -} diff --git a/host/apps/omap_debug/usrp-e-debug-pins.c b/host/apps/omap_debug/usrp-e-debug-pins.c deleted file mode 100644 index d18bbf990..000000000 --- a/host/apps/omap_debug/usrp-e-debug-pins.c +++ /dev/null @@ -1,77 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_gpio <string> - -static int fp; - -static int read_reg(__u16 reg) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - return d.buf[0]; -} - -static void write_reg(__u16 reg, __u16 val) -{ - int ret; - struct usrp_e_ctl16 d; - - d.offset = reg; - d.count = 1; - d.buf[0] = val; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); -} - -int main(int argc, char *argv[]) -{ - int test; - - test = 0; - if (argc < 2) { - printf("%s 0|1|off\n", argv[0]); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - - if (strcmp(argv[1], "0") == 0) { - printf("Selected 0 based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_TX_SEL, 0x0); - write_reg(UE_REG_GPIO_RX_SEL, 0x0); - write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); - } else if (strcmp(argv[1], "1") == 0) { - printf("Selected 1 based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DDR, 0xFFFF); - write_reg(UE_REG_GPIO_TX_SEL, 0xFFFF); - write_reg(UE_REG_GPIO_RX_SEL, 0xFFFF); - write_reg(UE_REG_GPIO_TX_DBG, 0xFFFF); - write_reg(UE_REG_GPIO_RX_DBG, 0xFFFF); - } else { - printf("Selected off based on %s\n", argv[1]); - write_reg(UE_REG_GPIO_TX_DDR, 0x0); - write_reg(UE_REG_GPIO_RX_DDR, 0x0); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-i2c.c b/host/apps/omap_debug/usrp-e-i2c.c deleted file mode 100644 index da8709ae1..000000000 --- a/host/apps/omap_debug/usrp-e-i2c.c +++ /dev/null @@ -1,87 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_i2c w address data0 data1 data 2 .... -// Usage: usrp_e_i2c r address count - -int main(int argc, char *argv[]) -{ - int fp, ret, i, tmp; - struct usrp_e_i2c *i2c_msg; - int direction, address, count; - - if (argc < 3) { - printf("Usage: usrp-e-i2c w address data0 data1 data2 ...\n"); - printf("Usage: usrp-e-i2c r address count\n"); - printf("All addresses and data in hex.\n"); - exit(-1); - } - - if (strcmp(argv[1], "r") == 0) { - direction = 0; - } else if (strcmp(argv[1], "w") == 0) { - direction = 1; - } else { - return -1; - } - - sscanf(argv[2], "%X", &address); - printf("Address = %X\n", address); - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - -// sleep(1); - - if (direction) { - count = argc - 3; - } else { - sscanf(argv[3], "%X", &count); - } - printf("Count = %X\n", count); - - i2c_msg = malloc(sizeof(i2c_msg) + count * sizeof(char)); - - i2c_msg->addr = address; - i2c_msg->len = count; - - for (i = 0; i < count; i++) { - i2c_msg->data[i] = i; - } - - if (direction) { - // Write - - for (i=0; i<count; i++) { - sscanf(argv[3+i], "%X", &tmp); - i2c_msg->data[i] = tmp; - } - - ret = ioctl(fp, USRP_E_I2C_WRITE, i2c_msg); - printf("Return value from i2c_write ioctl: %d\n", ret); - } else { - // Read - - ret = ioctl(fp, USRP_E_I2C_READ, i2c_msg); - printf("Return value from i2c_read ioctl: %d\n", ret); - - printf("Ioctl: %d Data read :", ret); - for (i=0; i<count; i++) { - printf(" %X", i2c_msg->data[i]); - } - printf("\n"); - - } - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-lb-test.c b/host/apps/omap_debug/usrp-e-lb-test.c deleted file mode 100644 index 68848064e..000000000 --- a/host/apps/omap_debug/usrp-e-lb-test.c +++ /dev/null @@ -1,58 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> -#include <pthread.h> -#include <stdlib.h> -#include <unistd.h> -#include <stddef.h> -#include "usrp_e.h" - -// max length #define PKT_DATA_LENGTH 1016 - -int main(int argc, char *argv[]) -{ - struct usrp_transfer_frame *tx_data, *rx_data; - int i, fp, packet_data_length, cnt; - struct usrp_e_ctl16 d; - - if (argc < 2) { - printf("%s data_size (in bytes < 2040)\n", argv[0]); - return -1; - } - - packet_data_length = atoi(argv[1]); - - fp = open("/dev/usrp_e0", O_RDWR); - - d.offset = 14; - d.count = 1; - d.buf[0] = (1 << 13); - ioctl(fp, USRP_E_WRITE_CTL16, &d); - - tx_data = malloc(2048); - rx_data = malloc(2048); - - tx_data->status = 0; - tx_data->len = sizeof(struct usrp_transfer_frame) + packet_data_length; - - while (1) { - - for (i = 0; i < packet_data_length; i++) { - tx_data->buf[i] = random() >> 24; - - } - - cnt = write(fp, tx_data, 2048); - cnt = read(fp, rx_data, 2048); - - if (tx_data->len != rx_data->len) - printf("Bad frame length sent %d, read %d\n", tx_data->len, rx_data->len); - - for (i = 0; i < packet_data_length; i++) { - if (tx_data->buf[i] != rx_data->buf[i]) - printf("Bad data at %d, sent %d, received %d\n", i, tx_data->buf[i], rx_data->buf[i]); - } - printf("---------------------------------------------------\n"); - sleep(1); - } -} diff --git a/host/apps/omap_debug/usrp-e-led.c b/host/apps/omap_debug/usrp-e-led.c deleted file mode 100644 index d1b6c8996..000000000 --- a/host/apps/omap_debug/usrp-e-led.c +++ /dev/null @@ -1,35 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> -#include <unistd.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, i, ret; - struct usrp_e_ctl16 d; - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - d.offset = UE_REG_MISC_BASE; - d.count = 1; - - while (1) { - for (i=0; i<8; i++) { - d.buf[0] = i; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - sleep(1); - } - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-ram.c b/host/apps/omap_debug/usrp-e-ram.c deleted file mode 100644 index d548f7ccd..000000000 --- a/host/apps/omap_debug/usrp-e-ram.c +++ /dev/null @@ -1,25 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, i, cnt; - unsigned short buf[1024]; - unsigned short buf_rb[1024]; - - fp = open("/dev/usrp1_e0", O_RDWR); - printf("fp = %d\n", fp); - - for (i=0; i<1024; i++) - buf[i] = i*256; - write(fp, buf, 2048); - read(fp, buf_rb, 2048); - - printf("Read back %hX %hX\n", buf_rb[0], buf_rb[1]); - - for (i=0; i<1024; i++) { - if (buf[i] != buf_rb[i]) - printf("Read - %hX, expected - %hX\n", buf_rb[i], buf[i]); - } -} diff --git a/host/apps/omap_debug/usrp-e-read.c b/host/apps/omap_debug/usrp-e-read.c deleted file mode 100644 index c28f018d5..000000000 --- a/host/apps/omap_debug/usrp-e-read.c +++ /dev/null @@ -1,18 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, cnt; - short buf[1024]; - - fp = open("/dev/usrp1_e0", O_RDONLY); - printf("fp = %d\n", fp); - - do { - cnt = read(fp, buf, 2048); -// printf("Bytes read - %d\n", cnt); - } while(1); - printf("Data - %hX\n", buf[0]); -} diff --git a/host/apps/omap_debug/usrp-e-spi.c b/host/apps/omap_debug/usrp-e-spi.c deleted file mode 100644 index c353c409b..000000000 --- a/host/apps/omap_debug/usrp-e-spi.c +++ /dev/null @@ -1,54 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <fcntl.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" - -// Usage: usrp_e_spi w|rb slave data - -int main(int argc, char *argv[]) -{ - int fp, slave, length, ret; - unsigned int data; - struct usrp_e_spi spi_dat; - - if (argc < 5) { - printf("Usage: usrp_e_spi w|rb slave transfer_length data\n"); - exit(-1); - } - - slave = atoi(argv[2]); - length = atoi(argv[3]); - data = atoll(argv[4]); - - printf("Data = %X\n", data); - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - if (fp < 0) { - perror("Open failed"); - return -1; - } - -// sleep(1); - - - spi_dat.slave = slave; - spi_dat.data = data; - spi_dat.length = length; - spi_dat.flags = UE_SPI_PUSH_FALL | UE_SPI_LATCH_RISE; - - if (*argv[1] == 'r') { - spi_dat.readback = 1; - ret = ioctl(fp, USRP_E_SPI, &spi_dat); - printf("Ioctl returns: %d, Data returned = %d\n", ret, spi_dat.data); - } else { - spi_dat.readback = 0; - ioctl(fp, USRP_E_SPI, &spi_dat); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-uart-rx.c b/host/apps/omap_debug/usrp-e-uart-rx.c deleted file mode 100644 index 24b417980..000000000 --- a/host/apps/omap_debug/usrp-e-uart-rx.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, ret; - struct usrp_e_ctl16 d; - __u16 clkdiv; - - if (argc == 0) { - printf("Usage: usrp-e-uart-rx <opt clkdiv>\n"); - printf("clkdiv = 278 is 230.4k \n"); - printf("clkdiv = 556 is 115.2k \n"); - exit(-1); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (argc == 2) { - clkdiv = atoi(argv[1]); - d.offset = UE_REG_UART_CLKDIV; - d.count = 1; - d.buf[0] = clkdiv; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - } - - while(1) { - d.offset = UE_REG_UART_RXLEVEL; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - - if (d.buf[0] > 0) { - d.offset = UE_REG_UART_RXCHAR; - d.count = 1; - ret = ioctl(fp, USRP_E_READ_CTL16, &d); - printf("%c", d.buf[0]); - fflush(stdout); - } - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-uart.c b/host/apps/omap_debug/usrp-e-uart.c deleted file mode 100644 index 2956c407f..000000000 --- a/host/apps/omap_debug/usrp-e-uart.c +++ /dev/null @@ -1,48 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <fcntl.h> -#include <string.h> -#include <sys/ioctl.h> - -#include "usrp_e.h" -#include "usrp_e_regs.hpp" - -// Usage: usrp_e_uart <string> - - -int main(int argc, char *argv[]) -{ - int fp, i, ret; - struct usrp_e_ctl16 d; - char *str = argv[1]; - __u16 clkdiv; - - if (argc < 2) { - printf("Usage: usrp_e_uart <string> <opt clkdiv>\n"); - printf("clkdiv = 278 is 230.4k \n"); - printf("clkdiv = 556 is 115.2k \n"); - exit(-1); - } - - fp = open("/dev/usrp_e0", O_RDWR); - printf("fp = %d\n", fp); - - if (argc == 3) { - clkdiv = atoi(argv[2]); - d.offset = UE_REG_UART_CLKDIV; - d.count = 1; - d.buf[0] = clkdiv; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - } - - for (i=0; i<strlen(str); i++) { - d.offset = UE_REG_UART_TXCHAR; - d.count = 1; - d.buf[0] = str[i]; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - printf("Wrote %X, to %X, ret = %d\n", d.buf[0], d.offset, ret); - } - - return 0; -} diff --git a/host/apps/omap_debug/usrp-e-write.c b/host/apps/omap_debug/usrp-e-write.c deleted file mode 100644 index 903c0071f..000000000 --- a/host/apps/omap_debug/usrp-e-write.c +++ /dev/null @@ -1,21 +0,0 @@ -#include <stdio.h> -#include <sys/types.h> -#include <fcntl.h> - -int main(int rgc, char *argv[]) -{ - int fp, i, cnt; - short buf[1024]; - - fp = open("/dev/usrp1_e0", O_WRONLY); - printf("fp = %d\n", fp); - - for (i=0; i<1024; i++) { - buf[i] = i; - } - -// do { - cnt = write(fp, buf, 2048); - printf("Bytes written - %d\n", cnt); -// } while (1); -} diff --git a/host/apps/omap_debug/usrp_e.h b/host/apps/omap_debug/usrp_e.h deleted file mode 100644 index 2c4aa2ac1..000000000 --- a/host/apps/omap_debug/usrp_e.h +++ /dev/null @@ -1,60 +0,0 @@ - -/* - * Copyright (C) 2010 Ettus Research, LLC - * - * Written by Philip Balister <philip@opensdr.com> - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __USRP_E_H -#define __USRP_E_H - -#include <linux/types.h> -#include <linux/ioctl.h> - -struct usrp_e_ctl16 { - __u32 offset; - __u32 count; - __u16 buf[20]; -}; - -struct usrp_e_ctl32 { - __u32 offset; - __u32 count; - __u32 buf[10]; -}; - -#define USRP_E_IOC_MAGIC 'u' -#define USRP_E_WRITE_CTL16 _IOW(USRP_E_IOC_MAGIC, 0x20, struct usrp_e_ctl16) -#define USRP_E_READ_CTL16 _IOWR(USRP_E_IOC_MAGIC, 0x21, struct usrp_e_ctl16) -#define USRP_E_WRITE_CTL32 _IOW(USRP_E_IOC_MAGIC, 0x22, struct usrp_e_ctl32) -#define USRP_E_READ_CTL32 _IOWR(USRP_E_IOC_MAGIC, 0x23, struct usrp_e_ctl32) -#define USRP_E_GET_RB_INFO _IOR(USRP_E_IOC_MAGIC, 0x27, struct usrp_e_ring_buffer_size_t) -#define USRP_E_GET_COMPAT_NUMBER _IO(USRP_E_IOC_MAGIC, 0x28) - -#define USRP_E_COMPAT_NUMBER 2 - -/* Flag defines */ -#define RB_USER (1<<0) -#define RB_KERNEL (1<<1) -#define RB_OVERRUN (1<<2) -#define RB_DMA_ACTIVE (1<<3) -#define RB_USER_PROCESS (1<<4) - -struct ring_buffer_info { - int flags; - int len; -}; - -struct usrp_e_ring_buffer_size_t { - int num_pages_rx_flags; - int num_rx_frames; - int num_pages_tx_flags; - int num_tx_frames; -}; - -#endif diff --git a/host/cmake/Modules/CheckCXXSymbolExistsCopy.cmake b/host/cmake/Modules/CheckCXXSymbolExistsCopy.cmake index d2cfac55e..2f16ba677 100644 --- a/host/cmake/Modules/CheckCXXSymbolExistsCopy.cmake +++ b/host/cmake/Modules/CheckCXXSymbolExistsCopy.cmake @@ -1,26 +1,33 @@ -# - Check if a symbol exists as a function, variable, or macro in C++ +#.rst: +# CheckCXXSymbolExists +# -------------------- +# +# Check if a symbol exists as a function, variable, or macro in C++ +# # CHECK_CXX_SYMBOL_EXISTS(<symbol> <files> <variable>) # # Check that the <symbol> is available after including given header -# <files> and store the result in a <variable>. Specify the list -# of files in one argument as a semicolon-separated list. -# CHECK_CXX_SYMBOL_EXISTS() can be used to check in C++ files, as opposed -# to CHECK_SYMBOL_EXISTS(), which works only for C. +# <files> and store the result in a <variable>. Specify the list of +# files in one argument as a semicolon-separated list. +# CHECK_CXX_SYMBOL_EXISTS() can be used to check in C++ files, as +# opposed to CHECK_SYMBOL_EXISTS(), which works only for C. # # If the header files define the symbol as a macro it is considered -# available and assumed to work. If the header files declare the -# symbol as a function or variable then the symbol must also be -# available for linking. If the symbol is a type or enum value -# it will not be recognized (consider using CheckTypeSize or -# CheckCSourceCompiles). -# -# The following variables may be set before calling this macro to -# modify the way the check is run: -# -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories -# CMAKE_REQUIRED_LIBRARIES = list of libraries to link +# available and assumed to work. If the header files declare the symbol +# as a function or variable then the symbol must also be available for +# linking. If the symbol is a type or enum value it will not be +# recognized (consider using CheckTypeSize or CheckCSourceCompiles). +# +# The following variables may be set before calling this macro to modify +# the way the check is run: +# +# :: +# +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link +# CMAKE_REQUIRED_QUIET = execute quietly without messages #============================================================================= # Copyright 2003-2011 Kitware, Inc. diff --git a/host/cmake/Modules/CheckSymbolExistsCopy.cmake b/host/cmake/Modules/CheckSymbolExistsCopy.cmake index 183b2bbe6..79c5ba78e 100644 --- a/host/cmake/Modules/CheckSymbolExistsCopy.cmake +++ b/host/cmake/Modules/CheckSymbolExistsCopy.cmake @@ -1,26 +1,35 @@ -# - Check if a symbol exists as a function, variable, or macro +#.rst: +# CheckSymbolExists +# ----------------- +# +# Check if a symbol exists as a function, variable, or macro +# # CHECK_SYMBOL_EXISTS(<symbol> <files> <variable>) # # Check that the <symbol> is available after including given header -# <files> and store the result in a <variable>. Specify the list -# of files in one argument as a semicolon-separated list. +# <files> and store the result in a <variable>. Specify the list of +# files in one argument as a semicolon-separated list. +# <variable> will be created as an internal cache variable. # # If the header files define the symbol as a macro it is considered -# available and assumed to work. If the header files declare the -# symbol as a function or variable then the symbol must also be -# available for linking. If the symbol is a type or enum value -# it will not be recognized (consider using CheckTypeSize or -# CheckCSourceCompiles). -# If the check needs to be done in C++, consider using CHECK_CXX_SYMBOL_EXISTS(), -# which does the same as CHECK_SYMBOL_EXISTS(), but in C++. +# available and assumed to work. If the header files declare the symbol +# as a function or variable then the symbol must also be available for +# linking. If the symbol is a type or enum value it will not be +# recognized (consider using CheckTypeSize or CheckCSourceCompiles). If +# the check needs to be done in C++, consider using +# CHECK_CXX_SYMBOL_EXISTS(), which does the same as +# CHECK_SYMBOL_EXISTS(), but in C++. +# +# The following variables may be set before calling this macro to modify +# the way the check is run: # -# The following variables may be set before calling this macro to -# modify the way the check is run: +# :: # -# CMAKE_REQUIRED_FLAGS = string of compile command line flags -# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) -# CMAKE_REQUIRED_INCLUDES = list of include directories -# CMAKE_REQUIRED_LIBRARIES = list of libraries to link +# CMAKE_REQUIRED_FLAGS = string of compile command line flags +# CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) +# CMAKE_REQUIRED_INCLUDES = list of include directories +# CMAKE_REQUIRED_LIBRARIES = list of libraries to link +# CMAKE_REQUIRED_QUIET = execute quietly without messages #============================================================================= # Copyright 2003-2011 Kitware, Inc. @@ -35,62 +44,70 @@ # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) -MACRO(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE) + + +macro(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE) _CHECK_SYMBOL_EXISTS("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" "${SYMBOL}" "${FILES}" "${VARIABLE}" ) -ENDMACRO(CHECK_SYMBOL_EXISTS) +endmacro() -MACRO(_CHECK_SYMBOL_EXISTS SOURCEFILE SYMBOL FILES VARIABLE) - IF("${VARIABLE}" MATCHES "^${VARIABLE}$") - SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") - SET(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) - IF(CMAKE_REQUIRED_LIBRARIES) - SET(CHECK_SYMBOL_EXISTS_LIBS - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - ELSE(CMAKE_REQUIRED_LIBRARIES) - SET(CHECK_SYMBOL_EXISTS_LIBS) - ENDIF(CMAKE_REQUIRED_LIBRARIES) - IF(CMAKE_REQUIRED_INCLUDES) - SET(CMAKE_SYMBOL_EXISTS_INCLUDES +macro(_CHECK_SYMBOL_EXISTS SOURCEFILE SYMBOL FILES VARIABLE) + if(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}") + set(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") + set(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) + if(CMAKE_REQUIRED_LIBRARIES) + set(CHECK_SYMBOL_EXISTS_LIBS + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + else() + set(CHECK_SYMBOL_EXISTS_LIBS) + endif() + if(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_SYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") - ELSE(CMAKE_REQUIRED_INCLUDES) - SET(CMAKE_SYMBOL_EXISTS_INCLUDES) - ENDIF(CMAKE_REQUIRED_INCLUDES) - FOREACH(FILE ${FILES}) - SET(CMAKE_CONFIGURABLE_FILE_CONTENT + else() + set(CMAKE_SYMBOL_EXISTS_INCLUDES) + endif() + foreach(FILE ${FILES}) + set(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") - ENDFOREACH(FILE) - SET(CMAKE_CONFIGURABLE_FILE_CONTENT - "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nvoid cmakeRequireSymbol(int dummy,...){(void)dummy;}\nint main()\n{\n#ifndef ${SYMBOL}\n cmakeRequireSymbol(0,&${SYMBOL});\n#endif\n return 0;\n}\n") + endforeach() + set(CMAKE_CONFIGURABLE_FILE_CONTENT + "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nint main(int argc, char** argv)\n{\n (void)argv;\n#ifndef ${SYMBOL}\n return ((int*)(&${SYMBOL}))[argc];\n#else\n (void)argc;\n return 0;\n#endif\n}\n") - CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" - "${SOURCEFILE}" @ONLY IMMEDIATE) + configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" + "${SOURCEFILE}" @ONLY) - MESSAGE(STATUS "Looking for ${SYMBOL}") - TRY_COMPILE(${VARIABLE} + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for ${SYMBOL}") + endif() + try_compile(${VARIABLE} ${CMAKE_BINARY_DIR} "${SOURCEFILE}" COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + ${CHECK_SYMBOL_EXISTS_LIBS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_SYMBOL_EXISTS_FLAGS} - "${CHECK_SYMBOL_EXISTS_LIBS}" "${CMAKE_SYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) - IF(${VARIABLE}) - MESSAGE(STATUS "Looking for ${SYMBOL} - found") - SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL}") - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log + if(${VARIABLE}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for ${SYMBOL} - found") + endif() + set(${VARIABLE} 1 CACHE INTERNAL "Have symbol ${SYMBOL}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the ${SYMBOL} " "exist passed with the following output:\n" "${OUTPUT}\nFile ${SOURCEFILE}:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") - ELSE(${VARIABLE}) - MESSAGE(STATUS "Looking for ${SYMBOL} - not found.") - SET(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL}") - FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + else() + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Looking for ${SYMBOL} - not found") + endif() + set(${VARIABLE} "" CACHE INTERNAL "Have symbol ${SYMBOL}") + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the ${SYMBOL} " "exist failed with the following output:\n" "${OUTPUT}\nFile ${SOURCEFILE}:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") - ENDIF(${VARIABLE}) - ENDIF("${VARIABLE}" MATCHES "^${VARIABLE}$") -ENDMACRO(_CHECK_SYMBOL_EXISTS) + endif() + endif() +endmacro() diff --git a/host/cmake/Modules/FindGPSD.cmake b/host/cmake/Modules/FindGPSD.cmake new file mode 100644 index 000000000..46fbc7b59 --- /dev/null +++ b/host/cmake/Modules/FindGPSD.cmake @@ -0,0 +1,62 @@ +# +# Copyright 2015 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# - Find libgps +# Find the Gpsd includes and client library +# This module defines +# LIBGPS_INCLUDE_DIR, where to find gps.h +# LIBGPS_LIBRARIES, the libraries needed by a GPSD client. +# LIBGPS_FOUND, If false, do not try to use GPSD. +# also defined, but not for general use are +# LIBGPS_LIBRARY, where to find the GPSD library. + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_GPSD "libgps") +PKG_CHECK_MODULES(PC_GPSD_V3_11 "libgps >= 3.11") + +IF(PC_GPSD_FOUND AND NOT PC_GPSD_V3_11_FOUND) + MESSAGE(WARNING "GPSD version found is too old") +ENDIF(PC_GPSD_FOUND AND NOT PC_GPSD_V3_11_FOUND) + +IF(PC_GPSD_V3_11_FOUND) + FIND_PATH( + LIBGPS_INCLUDE_DIR + NAMES gps.h + HINTS ${PC_GPSD_INCLUDE_DIR} + ) + + SET( + LIBGPS_NAMES + ${LIBGPS_NAMES} gps + ) + + FIND_LIBRARY( + LIBGPS_LIBRARY + NAMES ${LIBGPS_NAMES} + HINTS ${PC_GPSD_LIBDIR} + ) +ENDIF(PC_GPSD_V3_11_FOUND) + +# handle the QUIETLY and REQUIRED arguments and set LIBGPS_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBGPS DEFAULT_MSG LIBGPS_LIBRARY LIBGPS_INCLUDE_DIR) + +IF(LIBGPS_FOUND) + SET(LIBGPS_LIBRARIES ${LIBGPS_LIBRARY}) +ENDIF(LIBGPS_FOUND) + +MARK_AS_ADVANCED(LIBGPS_LIBRARY LIBGPS_INCLUDE_DIR) diff --git a/host/cmake/Modules/FindORC.cmake b/host/cmake/Modules/FindORC.cmake deleted file mode 100644 index e13eae235..000000000 --- a/host/cmake/Modules/FindORC.cmake +++ /dev/null @@ -1,36 +0,0 @@ -######################################################################## -# Find the library for ORC development files -######################################################################## - -INCLUDE(FindPkgConfig) -PKG_CHECK_MODULES(PC_ORC "orc-0.4") -PKG_CHECK_MODULES(PC_ORC_V4_11 "orc-0.4 > 0.4.11") - -#we are using the pkg config as a version check -#if we have pkg config, the right version must be found -#the alternative is that no pkg config orc is found -if (PC_ORC_V4_11_FOUND OR NOT PC_ORC_FOUND) - -FIND_PATH( - ORC_INCLUDE_DIRS - NAMES orc/orc.h - HINTS $ENV{ORC_DIR}/include/orc-0.4 - ${PC_ORC_INCLUDEDIR} - PATHS /usr/local/include/orc-0.4 - /usr/include/orc-0.4 -) - -FIND_LIBRARY( - ORC_LIBRARIES - NAMES orc-0.4 - HINTS $ENV{ORC_DIR}/lib - ${PC_ORC_LIBDIR} - PATHS /usr/local/lib - /usr/lib -) - -endif() #both PC ORC FOUND - -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(ORC DEFAULT_MSG ORC_LIBRARIES ORC_INCLUDE_DIRS) -MARK_AS_ADVANCED(ORC_LIBRARIES ORC_INCLUDE_DIRS) diff --git a/host/cmake/Modules/FindUSB1.cmake b/host/cmake/Modules/FindUSB1.cmake index 1e8e3ba03..7c4a8fb6b 100644 --- a/host/cmake/Modules/FindUSB1.cmake +++ b/host/cmake/Modules/FindUSB1.cmake @@ -8,7 +8,7 @@ PKG_CHECK_MODULES(PC_LIBUSB QUIET libusb-1.0) FIND_PATH(LIBUSB_INCLUDE_DIRS NAMES libusb.h - HINTS $ENV{LIBUSB_DIR}/include $ENV{LIBUSB_DIR}/include/libusb-1.0 + HINTS $ENV{LIBUSB_DIR}/include $ENV{LIBUSB_DIR}/include/libusb-1.0 ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDEDIR}/libusb-1.0 PATHS /usr/local/include/libusb-1.0 /usr/local/include /usr/include/libusb-1.0 /usr/include @@ -52,6 +52,11 @@ if(HAVE_LIBUSB_ERROR_NAME) list(APPEND LIBUSB_DEFINITIONS "HAVE_LIBUSB_ERROR_NAME=1") endif(HAVE_LIBUSB_ERROR_NAME) +CHECK_FUNCTION_EXISTS("libusb_strerror" HAVE_LIBUSB_STRERROR) +if(HAVE_LIBUSB_STRERROR) + list(APPEND LIBUSB_DEFINITIONS "HAVE_LIBUSB_STRERROR=1") +endif(HAVE_LIBUSB_STRERROR) + include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUSB DEFAULT_MSG LIBUSB_LIBRARIES LIBUSB_INCLUDE_DIRS) MARK_AS_ADVANCED(LIBUSB_INCLUDE_DIRS LIBUSB_LIBRARIES) diff --git a/host/cmake/Modules/UHDComponent.cmake b/host/cmake/Modules/UHDComponent.cmake index bf4a50e33..5a7dd8c86 100644 --- a/host/cmake/Modules/UHDComponent.cmake +++ b/host/cmake/Modules/UHDComponent.cmake @@ -1,5 +1,5 @@ # -# Copyright 2010-2011,2013 Ettus Research LLC +# Copyright 2010-2011,2013,2015 Ettus Research LLC # # 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 @@ -26,18 +26,31 @@ SET(_uhd_disabled_components "" CACHE INTERNAL "" FORCE) # - enb the default enable setting # - deps a list of dependencies # - dis the default disable setting +# - req fail if dependencies not met (unless specifically disabled) ######################################################################## -MACRO(LIBUHD_REGISTER_COMPONENT name var enb deps dis) +MACRO(LIBUHD_REGISTER_COMPONENT name var enb deps dis req) MESSAGE(STATUS "") MESSAGE(STATUS "Configuring ${name} support...") FOREACH(dep ${deps}) MESSAGE(STATUS " Dependency ${dep} = ${${dep}}") ENDFOREACH(dep) + #if user specified option, store here + IF("${${var}}" STREQUAL "OFF") + SET(user_disabled TRUE) + ELSE() + SET(user_disabled FALSE) + ENDIF("${${var}}" STREQUAL "OFF") + #setup the dependent option for this component INCLUDE(CMakeDependentOption) CMAKE_DEPENDENT_OPTION(${var} "enable ${name} support" ${enb} "${deps}" ${dis}) + #if a required option's dependencies aren't met, fail unless user specifies otherwise + IF(NOT ${var} AND ${req} AND NOT user_disabled) + MESSAGE(FATAL_ERROR "Dependencies for required component ${name} not met.") + ENDIF(NOT ${var} AND ${req} AND NOT user_disabled) + #append the component into one of the lists IF(${var}) MESSAGE(STATUS " Enabling ${name} support.") diff --git a/host/cmake/Modules/UHDGlobalDefs.cmake b/host/cmake/Modules/UHDGlobalDefs.cmake index 78a69d46d..58c0b1287 100644 --- a/host/cmake/Modules/UHDGlobalDefs.cmake +++ b/host/cmake/Modules/UHDGlobalDefs.cmake @@ -20,7 +20,7 @@ INCLUDE(CheckCXXSymbolExistsCopy) ## Check for std::log2 -CHECK_CXX_SYMBOL_EXISTS("log2" "cmath" HAVE_LOG2) +CHECK_CXX_SYMBOL_EXISTS(log2 cmath HAVE_LOG2) ## Macros for the version number IF(UHD_VERSION_DEVEL) diff --git a/host/cmake/Modules/UHDPackage.cmake b/host/cmake/Modules/UHDPackage.cmake index 298b9d33a..640935830 100644 --- a/host/cmake/Modules/UHDPackage.cmake +++ b/host/cmake/Modules/UHDPackage.cmake @@ -99,6 +99,8 @@ IF(${CPACK_GENERATOR} STREQUAL NSIS) SET(MSVC_VERSION "VS2012") ELSEIF(MSVC12) # Visual Studio 2013 (12.0) SET(MSVC_VERSION "VS2013") + ELSEIF(MSVC14) # Visual Studio 2015 (14.0) + SET(MSVC_VERSION "VS2015") ENDIF() SET(CPACK_PACKAGE_FILE_NAME "uhd_${UHD_VERSION}_Win${BIT_WIDTH}_${MSVC_VERSION}") ELSE() @@ -210,10 +212,10 @@ SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " DeleteRegValue HKLM ${HLKM_ENV} \\\"UHD_PKG_PATH\\\" ") -IF(MSVC) - #Install necessary MSVC runtime DLL's +IF(WIN32) + #Install necessary runtime DLL's INCLUDE(InstallRequiredSystemLibraries) -ENDIF(MSVC) +ENDIF(WIN32) ######################################################################## IF(NOT ${CPACK_GENERATOR} STREQUAL NSIS) diff --git a/host/cmake/Modules/UHDPython.cmake b/host/cmake/Modules/UHDPython.cmake index fdcdccb4b..a3da150a1 100644 --- a/host/cmake/Modules/UHDPython.cmake +++ b/host/cmake/Modules/UHDPython.cmake @@ -51,7 +51,7 @@ MESSAGE(STATUS "Python interpreter: ${PYTHON_EXECUTABLE}") MESSAGE(STATUS "Override with: -DPYTHON_EXECUTABLE=<path-to-python>") IF(NOT PYTHONINTERP_FOUND) - MESSAGE(FATAL_ERROR "Error: Python interpretor required by the build system.") + MESSAGE(FATAL_ERROR "Error: Python interpreter required by the build system.") ENDIF(NOT PYTHONINTERP_FOUND) MACRO(PYTHON_CHECK_MODULE desc mod cmd have) diff --git a/host/cmake/Modules/UHDUnitTest.cmake b/host/cmake/Modules/UHDUnitTest.cmake index 76fec14b8..f3e848906 100644 --- a/host/cmake/Modules/UHDUnitTest.cmake +++ b/host/cmake/Modules/UHDUnitTest.cmake @@ -1,5 +1,5 @@ # -# Copyright 2010-2012 Ettus Research LLC +# Copyright 2010-2012,2015 Ettus Research LLC # # 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 @@ -18,31 +18,30 @@ ######################################################################## # Add a unit test and setup the environment for a unit test. # Takes the same arguments as the ADD_TEST function. -# -# Before calling set the following variables: -# UHD_TEST_TARGET_DEPS - built targets for the library path -# UHD_TEST_LIBRARY_DIRS - directories for the library path ######################################################################## function(UHD_ADD_TEST test_name) #Ensure that the build exe also appears in the PATH. list(APPEND UHD_TEST_TARGET_DEPS ${ARGN}) - #In the land of windows, all libraries must be in the PATH. - #Since the dependent libraries are not yet installed, - #we must manually set them in the PATH to run tests. - #The following appends the path of a target dependency. - foreach(target ${UHD_TEST_TARGET_DEPS}) - get_target_property(location ${target} LOCATION) - if(location) - get_filename_component(path ${location} PATH) - string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) - list(APPEND UHD_TEST_LIBRARY_DIRS ${path}) - endif(location) - endforeach(target) - - file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir) - file(TO_NATIVE_PATH "${UHD_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? + #We need to put the directory with the .so/.dll file in the + #appropriate environment variable, as well as the test + #directory itself. + if(WIN32) + set(UHD_TEST_LIBRARY_DIRS + "${Boost_LIBRARY_DIRS}" + "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" + "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}" + ) + else() + set(UHD_TEST_LIBRARY_DIRS + "${Boost_LIBRARY_DIRS}" + "${CMAKE_BINARY_DIR}/lib" + "${CMAKE_CURRENT_BINARY_DIR}" + ) + endif(WIN32) + + file(TO_NATIVE_PATH "${UHD_TEST_LIBRARY_DIRS}" libpath) #http://www.cmake.org/pipermail/cmake/2009-May/029464.html #Replaced this add test + set environs code with the shell script generation. diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 66b4402ed..2767bf110 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -27,8 +27,8 @@ FIND_PACKAGE(Git QUIET) # - set UHD_VERSION_DEVEL to true for master and development branches ######################################################################## SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 008) -SET(UHD_VERSION_PATCH 005) +SET(UHD_VERSION_MINOR 009) +SET(UHD_VERSION_PATCH 000) SET(UHD_VERSION_DEVEL FALSE) ######################################################################## @@ -38,7 +38,7 @@ SET(UHD_VERSION_DEVEL FALSE) FUNCTION(DEPAD_NUM input_num output_num) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print int('${input_num}')" + COMMAND ${PYTHON_EXECUTABLE} -c "print(int('${input_num}'))" OUTPUT_VARIABLE depadded_num OUTPUT_STRIP_TRAILING_WHITESPACE ) SET(${output_num} ${depadded_num} PARENT_SCOPE) @@ -69,12 +69,12 @@ EXECUTE_PROCESS( IF(_git_describe_result EQUAL 0) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[-2]" + COMMAND ${PYTHON_EXECUTABLE} -c "print('${_git_describe}'.split('-')[-2])" OUTPUT_VARIABLE UHD_GIT_COUNT OUTPUT_STRIP_TRAILING_WHITESPACE ) EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print '${_git_describe}'.split('-')[-1]" + COMMAND ${PYTHON_EXECUTABLE} -c "print('${_git_describe}'.split('-')[-1])" OUTPUT_VARIABLE UHD_GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ) ENDIF() diff --git a/host/cmake/Toolchains/mingw_cross.cmake b/host/cmake/Toolchains/mingw_cross.cmake new file mode 100644 index 000000000..f1406ec46 --- /dev/null +++ b/host/cmake/Toolchains/mingw_cross.cmake @@ -0,0 +1,65 @@ +# +# Allow the user to specify a MinGW prefix, but fill in +# most likely default if none given. +# + +SET(CMAKE_SYSTEM_NAME Windows) + +IF(NOT DEFINED MINGW_PREFIX) + SET(POSSIBLE_PREFIXES + i586-mingw32msvc + i686-pc-mingw32 + x86_64-pc-mingw32 + i686-w64-mingw32 + x86_64-w64-mingw32 + ) + + SET(MINGW_FOUND 0) + FOREACH(prefix ${POSSIBLE_PREFIXES}) + IF(EXISTS /usr/${prefix}) + SET(MINGW_PREFIX ${prefix}) + SET(MINGW_FOUND 1) + BREAK() + ENDIF(EXISTS /usr/${prefix}) + ENDFOREACH(prefix ${POSSIBLE_PREFIXES}) + + IF(NOT MINGW_FOUND) + MESSAGE(FATAL_ERROR "No MinGW type specified, but none detected in the usual locations.") + ENDIF(NOT MINGW_FOUND) +ENDIF(NOT DEFINED MINGW_PREFIX) + +SET(MINGW_PREFIX ${MINGW_PREFIX} CACHE STRING "MinGW prefix") + +SET(CMAKE_C_COMPILER ${MINGW_PREFIX}-gcc) +SET(CMAKE_CXX_COMPILER ${MINGW_PREFIX}-g++) +SET(CMAKE_RC_COMPILER ${MINGW_PREFIX}-windres) + +IF(NOT DEFINED CMAKE_FIND_ROOT_PATH) + SET(CMAKE_FIND_ROOT_PATH /usr/${MINGW_PREFIX}) +ENDIF(NOT DEFINED CMAKE_FIND_ROOT_PATH) + +SET(CMAKE_INCLUDE_PATH + ${CMAKE_FIND_ROOT_PATH}/local/include + ${CMAKE_FIND_ROOT_PATH}/include +) + +SET(CMAKE_LIBRARY_PATH + ${CMAKE_FIND_ROOT_PATH}/local/lib + ${CMAKE_FIND_ROOT_PATH}/lib +) + +# Adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Tell pkg-config not to look at the target environment's .pc files. +# Setting PKG_CONFIG_LIBDIR sets the default search directory, but we have to +# set PKG_CONFIG_PATH as well to prevent pkg-config falling back to the host's +# path. +set(ENV{PKG_CONFIG_LIBDIR} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig) +set(ENV{PKG_CONFIG_PATH} ${CMAKE_FIND_ROOT_PATH}/lib/pkgconfig) + +set(ENV{MINGDIR} ${CMAKE_FIND_ROOT_PATH}) diff --git a/host/cmake/Toolchains/oe-sdk_cross.cmake b/host/cmake/Toolchains/oe-sdk_cross.cmake index ea77815c9..f8ef0157c 100644 --- a/host/cmake/Toolchains/oe-sdk_cross.cmake +++ b/host/cmake/Toolchains/oe-sdk_cross.cmake @@ -9,5 +9,3 @@ set( CMAKE_FIND_ROOT_PATH $ENV{OECORE_NATIVE_SYSROOT} $ENV{OECORE_TARGET_SYSROOT set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -set ( ORC_INCLUDE_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/include/orc-0.4 ) -set ( ORC_LIBRARY_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/lib ) diff --git a/host/cmake/msvc/stdbool.h b/host/cmake/msvc/stdbool.h new file mode 100644 index 000000000..3a91eea37 --- /dev/null +++ b/host/cmake/msvc/stdbool.h @@ -0,0 +1,33 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_MSC_STDBOOL_H +#define INCLUDED_MSC_STDBOOL_H + +#ifndef _MSC_VER +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif + +#ifndef __cplusplus + +#define bool int +#define true 1 +#define false 0 + +#endif + +#endif /* INCLUDED_MSC_STDBOOL_H */ diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index d0f060ceb..3090d3f80 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2013,2015 Ettus Research LLC # # 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 @@ -28,7 +28,7 @@ SET(ENABLE_MANUAL_OR_DOXYGEN false) ######################################################################## # Setup Manual (using Doxygen) ######################################################################## -LIBUHD_REGISTER_COMPONENT("Manual" ENABLE_MANUAL ON "DOXYGEN_FOUND" OFF) +LIBUHD_REGISTER_COMPONENT("Manual" ENABLE_MANUAL ON "DOXYGEN_FOUND" OFF OFF) # Check if fpga submodule is there. If yes: # - Add fpga-src/docs and fpga-src/README.md to inputs @@ -64,7 +64,7 @@ ENDIF(ENABLE_MANUAL) # Setup API documentation (using Doxygen) ######################################################################## MESSAGE(STATUS "") -LIBUHD_REGISTER_COMPONENT("API/Doxygen" ENABLE_DOXYGEN ON "DOXYGEN_FOUND" OFF) +LIBUHD_REGISTER_COMPONENT("API/Doxygen" ENABLE_DOXYGEN ON "DOXYGEN_FOUND" OFF OFF) OPTION(ENABLE_DOXYGEN_FULL "Use Doxygen to document the entire source tree (not just API)" OFF) IF(LIBUHDDEV_PKG) @@ -75,6 +75,8 @@ IF(ENABLE_DOXYGEN) SET(ENABLE_MANUAL_OR_DOXYGEN true) #make doxygen directory depend on the header files FILE(GLOB_RECURSE header_files ${CMAKE_SOURCE_DIR}/include/*.hpp) + FILE(GLOB_RECURSE h_files ${CMAKE_SOURCE_DIR}/include/*.h) + LIST(APPEND header_files ${h_files}) SET(DOXYGEN_DEPENDENCIES ${DOXYGEN_DEPENDENCIES} ${header_files}) IF(ENABLE_DOXYGEN_FULL) SET(DOXYGEN_INPUT_DIRS "${DOXYGEN_INPUT_DIRS} ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/lib") @@ -120,6 +122,7 @@ SET(man_page_sources uhd_cal_tx_dc_offset.1 uhd_cal_tx_iq_balance.1 uhd_find_devices.1 + uhd_image_loader.1 uhd_images_downloader.1 uhd_usrp_probe.1 usrp_n2xx_simple_net_burner.1 @@ -138,7 +141,7 @@ IF(NOT WIN32) SET(NOT_WIN32 TRUE) ENDIF(NOT WIN32) -LIBUHD_REGISTER_COMPONENT("Man Pages" ENABLE_MAN_PAGES ON "GZIP_FOUND;NOT_WIN32" OFF) +LIBUHD_REGISTER_COMPONENT("Man Pages" ENABLE_MAN_PAGES ON "GZIP_FOUND;NOT_WIN32" OFF OFF) IF(ENABLE_MAN_PAGES) #Generate man pages diff --git a/host/docs/build.dox b/host/docs/build.dox index 1097ab5ab..0e722e376 100644 --- a/host/docs/build.dox +++ b/host/docs/build.dox @@ -11,7 +11,7 @@ package manager. <b>Mac OS X Notes:</b> Install the Xcode app to get the build tools (GCC and Make). -Use MacPorts to get the Boost and Cheetah dependencies. +Use MacPorts to get the Boost and Mako dependencies. Other dependencies can be downloaded as DMG installers from the web or installed via MacPorts. See the UHD OS X build instructions for more information: \ref build_instructions_osx @@ -28,7 +28,7 @@ The following compilers are known to work and officially supported: - GCC >= 4.4 - Clang >= 3.1 -- MSVC >= 2010 +- MSVC >= 2012 Other compilers (or lower versions) may work, but are unsupported. @@ -57,22 +57,20 @@ Other compilers (or lower versions) may work, but are unsupported. ### Python -- **Purpose:** used by Cheetah and utility scripts +- **Purpose:** used by mako and utility scripts - **Minimum Version:** 2.6 - **Usage:** build time + runtime utility scripts (required) - **Download URL:** http://www.python.org/download/ -### Cheetah +### Mako - **Purpose:** source code generation -- **Minimum Version:** 2.0 +- **Minimum Version:** 0.5.0 - **Usage:** build time (required) -- **Download URL:** http://www.cheetahtemplate.org/download.html -- **Download URL (Windows installer):** http://feisley.com/python/cheetah/ +- **Download URL:** http://www.makotemplates.org/download.html **Alternative method:** -Install **setuptools**, and use the **easy_install** command to install Cheetah. -http://pypi.python.org/pypi/setuptools +You can use `pip` or `easy_install` to install Mako from PyPi. ### Doxygen @@ -96,7 +94,7 @@ or install msysGit from http://code.google.com/p/msysgit/downloads/list. You can install all the dependencies through the package manager: - sudo apt-get install libboost-all-dev libusb-1.0-0-dev python-cheetah doxygen python-docutils cmake + sudo apt-get install libboost-all-dev libusb-1.0-0-dev python-mako doxygen python-docutils cmake build-essential Your actual command may differ. @@ -104,7 +102,7 @@ Your actual command may differ. You can install all the dependencies through the package manager: - sudo yum -y install boost-devel libusb1-devel python-cheetah doxygen python-docutils cmake + sudo yum -y install boost-devel libusb1-devel python-mako doxygen python-docutils cmake make gcc gcc-c++ Your actual command may differ. diff --git a/host/docs/c_api.dox b/host/docs/c_api.dox new file mode 100644 index 000000000..5b5790f21 --- /dev/null +++ b/host/docs/c_api.dox @@ -0,0 +1,86 @@ +/*! \page page_c_api UHD - C API + +\tableofcontents + +\section c_api_intro Introduction + +Alongside its C++ API, UHD provides a C API wrapper for the uhd::usrp::multi_usrp and +uhd::usrp_clock::multi_usrp_clock classes, as well as their associated classes and +structs. Other important UHD functions are also included in this API. To use this +API, simply: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c} +#include <uhd.h> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +...and all UHD C-level structs and functions will be available to you. The sections below +give more detail on the key features of the C API. + +\subsection c_api_handles C-Level Handles + +Most of the UHD classes that can be accessed on the C level are done so through handles, +which internally store the C++ representation and allow access to internal values +through helper functions. + +All handles have associated *_make() and *_free() functions. After creating a handle, it must +be passed through its make() function before it can be used in your program. Before the program +terminates, you must pass the handle into its free() function, or your program will have a memory +leak. The example below shows the proper usage of an RX metadata handle over the course of its +lifetime, from instantiation to destruction. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +uhd_rx_metadata_handle md; +uhd_rx_metadata_make(&md); + +// Streaming here puts useful information into metadata +time_t full_secs; +double frac_secs; +uhd_rx_metadata_time_spec(md, &full_secs, &frac_secs); + +uhd_rx_metadata_free(&md); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Again, make sure to pass your handle into a make() function before using it, or you will +run into undefined behavior. Also be careful not to use the handle after passing it into +a free() function, or your program will segfault. + +\subsection c_api_errorcode Error Codes + +As C cannot handle C++ runtime exceptions, UHD's C wrapper functions catch all exceptions +and translate them into error codes, which are returned by each function. Any output variables +are passed in as pointers into the function, which will set them internally. + +Each uhd::runtime_error has a corresponding ::uhd_error value. Separate error codes indicate +that a boost::exception or std::exception has been thrown, and any other exceptions are +indicated by a catch-all ::UHD_ERROR_UNKNOWN code. + +All UHD C-level handles store the string representation of the last C++ exception thrown internally. +These handles have corresponding *_get_last_error() functions that will place the error string into a +supplied string buffer. + +For example, if a USRP device's handle throws an exception internally, the following code can access +its error info: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} +char err_msg[256]; +uhd_usrp_handle usrp; +double gain; +// USRP configuration done here +uhd_error error_code = uhd_usrp_get_rx_gain(usrp, 0, "", &gain); +if(error_code){ + uhd_usrp_get_last_error(usrp, err_msg, 256); + fprintf(stderr, "Error code %d: %s\n", error_code, err_msg); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All error codes can be found in <uhd/error.h>. + +\subsection c_api_examples Example Code + +UHD provides two examples that demonstrate the typical use case of the C API: RX and TX streaming. +The <b>rx_samples_c</b> example is a simplified C version of <b>rx_samples_to_file</b>, +and the <b>tx_samples_c</b> example is a simplified C version of <b>tx_waveforms</b>. These examples +can be easily adapted or serve as a starting point for your own UHD C applications. + +*/ +// vim:ft=doxygen: diff --git a/host/docs/coding.dox b/host/docs/coding.dox index 32dbe944a..6a15098d7 100644 --- a/host/docs/coding.dox +++ b/host/docs/coding.dox @@ -16,6 +16,11 @@ The Multi-USRP-Clock class provides a high-level interface to a single clock device or set of clock devices, from which the time can be queried. See the documentation for uhd::usrp_clock::multi_usrp_clock. +\subsection coding_api_hilevelc High-Level: The C API + +Both USRP and clock devices can be interacted with using a C API wrapper included +by default in all UHD builds. More information can be found \subpage page_c_api "here". + \subsection coding_api_lowlevel Low-Level: The device API A device is an abstraction for hardware that is connected to the host diff --git a/host/docs/dboards.dox b/host/docs/dboards.dox index 3d6866a42..812a3a09e 100644 --- a/host/docs/dboards.dox +++ b/host/docs/dboards.dox @@ -372,6 +372,10 @@ Sensors: - **rssi**: float for measured RSSI in dBm - **temperature**: float for measured temperature in degC +\subsection dboards_e300 E310 MIMO XCVR board + +Please refer to \ref e3x0_dboard_e310. + \subsection dboards_dbsrxmod DBSRX - Modifying for other boards that USRP1 Due to different clocking capabilities, the DBSRX will require diff --git a/host/docs/general.dox b/host/docs/general.dox index e4ffcfb6e..3e9dfc63a 100644 --- a/host/docs/general.dox +++ b/host/docs/general.dox @@ -34,6 +34,8 @@ support this functionality are: - SBX-120 - CBX - CBX-120 +- UBX +- UBX-160 \subsubsection general_tuning_rxchain Tuning the receive chain: @@ -78,6 +80,45 @@ second). usrp->issue_stream_command(...); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\section general_sampleratenotes Sample rate notes + +Sample rates as delivered to the host computer for USRP devices are constrained to follow several important rules. + +It is important to understand that strictly-integer <b>decimation</b> and <b>interpolation</b> are used within USRP +hardware to meet the requested sample-rate requirements of the application at hand. That means that the desired +sample rate must meet the requirement that master-clock-rate/desired-sample-rate be an integer ratio. Further, it is +strongly desirable for that ratio to be <b>even</b>. + +There are further constraints on the desired sample rate, such that if the required decimation or interpolation exceeds 128, +then the resulting decimation <b>must</b> be evenly divisible by 2, and that if the required decimation exceeds 256, the +resulting decimation \b must be evenly divisible by 4. + +For USRP devices with fixed master clocks (notably: USRP1, USRP2, N2xx), there are fewer effective sample rates available than +on USRP hardware that provides some flexibility in selecting a master clock. Several USRP devices support flexible master +clock selection, allowing a broader range of sample rate selections by applications. See the individual devices' manual +pages for more details. + +In many cases using USRPs with flexible master-clock rates, it is possible to achieve lower sample rates without running into +the constraints of higher decimations, simply by choosing a lower master-clock rate to keep required decimation below 128. + +\subsection general_sampleratenotes_automatic Automatic master-clock selection + +In recent versions of UHD software (3.8.5 and newer), and on some devices (currently: B2xx and E3xx series), +the master clock rate is chosen automatically (unless specified by the user). +UHD will select a master clock rate that is consistent with the desired sample rate indicated by the application. + +\subsection general_sampleratenotes_nyquist Master clock rate and Nyquist + +In selecting a master clock rate on certain USRP hardware (X3xx and B1xx), it is important to select a rate that still provides +correct alias suppression by the analog hardware. For daughtercards with a 40 MHz analog bandwidth, this means the clock rate +must be <b>at least</b> 40 MHz, with better performance to be expected with a higher clock rate. For daughtercards +with 160 MHz bandwidth, it must be <b>at least</b> 160 MHz, again, better performance is to expected with a higher clock rate. + +For hardware with fixed master clock rates, of course, this isn't a consideration. + +For B2xx and E3xx hardware, the alias suppression is handled differently by the AD936x RFIC, and master clock rate +is significantly more flexible as a result. + \section general_ounotes Overflow/Underflow Notes <b>Note:</b> The following overflow/underflow notes do not apply to USRP1, diff --git a/host/docs/ni_rio_kernel.dox b/host/docs/ni_rio_kernel.dox index d364cad3c..1fd2f0681 100644 --- a/host/docs/ni_rio_kernel.dox +++ b/host/docs/ni_rio_kernel.dox @@ -13,16 +13,16 @@ which must be loaded. Your kernel version must be supported by the kernel modules. Only 64-bit kernels are supported. -<b>Currently, the latest supported kernel version is 3.14.x.</b> +<b>Currently, the latest supported kernel version is 4.2.x.</b> \subsection linux_installation Installing the Drivers in Linux The NI USRP RIO installer can be found <a -href=http://files.ettus.com/binaries/niusrprio/niusrprio-installer.tar.gz>here</a>. +href=http://files.ettus.com/binaries/niusrprio/niusrprio-installer-15.0.0.tar.gz>here</a>. Download the installer and extract it with the following command: - tar zxf niusrprio-installer.tar.gz + tar zxf niusrprio-installer-15.0.0.tar.gz The files will be extracted into a directory called <b>niusrprio-installer</b>. @@ -57,10 +57,18 @@ a system restart, please create an init.d script that runs niusrprio_pcie start. \subsection linux_swapping Hot-Plugging and Power-Cycling +The USRP X3x0, NI USRP-294x and NI USRP-295x devices <b>cannot</b> be hot-swapped when connected over PCI Express. +Unplugging the PCI Express connection or powering the device should be done only after disabling the device or +powering off the host computer. + +<b>WARNING:</b> If the device is unplugged without running the `niusrprio_pcie stop` command, the system could become unstable. + +\subsection linux_upgrades Upgrading Kernels + The NI USRPRIO kernel modules are built for a specific kernel version. If you upgrade/downgrade the linux kernel on the host to a version different from the one that the installer was run on, then you may see the following error message -when running `niusrprio star`. +when running `niusrprio start`. ERROR: could not insert 'NiRioSrv': Unknown symbol in module, or unknown parameter (see dmesg) ERROR: could not insert 'niusrpriok': Unknown symbol @@ -69,15 +77,16 @@ when running `niusrprio star`. To rebuild the kernel modules for the currently running kernel, simple run the following - sudo /usr/local/bin/niusrprio_pcie stop sudo /usr/local/bin/updateNIDrivers - --no-prompt sudo /usr/local/bin/niusrprio_pcie start + sudo /usr/local/bin/niusrprio_pcie stop + sudo /usr/local/bin/updateNIDrivers --no-prompt + sudo /usr/local/bin/niusrprio_pcie start \subsection linux_uninstalling Uninstalling in Linux To uninstall the NI USRP RIO kernel modules and RPC server, run the following command: - sudo niusrprio-installer/UNINSTALL + sudo /usr/local/bin/niusrprio_uninstall Select y at the prompt, and the script will uninstall all installed components. @@ -98,17 +107,16 @@ The kernel driver is only supported on: \subsection win_install Installing NI-USRP in Windows -The NI-USRP 1.3 installer can be found <a -href="http://www.ni.com/download/ni-usrp-1.3/4711/en/">here</a> You will need to +The NI-USRP 14.5 installer can be found <a +href="http://search.ni.com/nisearch/app/main/p/bot/no/ap/global/lang/en/pg/1/q/NI-USRP%2014.5/">here</a> You will need to create a free NI User Account to download the installer. Perform the following steps to download and install the NI-USRP exe driver package: -- Choose the "2. Standard Download:NIUSRP130.exe" option to download -- NIUSRP130.exe to your computer Run NIUSRP130.exe as an Administrator and -- extract the contents to 'C:\\National Instruments Downloads\\NI-USRP\\1.3' In the -- extract location, run setup.exe and follow the prompts. +- Choose the "2. Standard Download:NIUSRP1450.zip" option to download NIUSRP1450.zip to your computer +- Extract the contents of NIUSRP1450.zip to 'C:\\National Instruments Downloads\\NI-USRP\\14.5' +- In the extract location, run setup.exe and follow the prompts. Reboot the computer after both the NI-USRP package has been installed. @@ -118,19 +126,16 @@ Once everything is installed and the system is rebooted, your X300/X310 PCI Express device should automatically be detected by the Windows Device Manager. The device should be enabled by default. -- To disable the USRPRIO device, navigate to "Device Manager", locate your -- USRPRIO-X3x0 device, right-click on it and choose "Disable". To enable the -- USRPRIO device, navigate to "Device Manager", locate your USRPRIO-X3x0 device, -- right-click on it and choose "Enable". +- To disable the USRPRIO device, navigate to "Device Manager", locate your USRPRIO-X3x0 device, right-click on it and choose "Disable" +- To enable the USRPRIO device, navigate to "Device Manager", locate your USRPRIO-X3x0 device, right-click on it and choose "Enable". \subsection win_swapping Hot-plugging and Power-cycling -The USRP X3x0, NI USRP-294x and NI USRP-295x devices <b>cannot</b> be hot-swapped -when connected over PCI Express. Unplugging the PCI Express connection or -powering the device should be done only after disabling the device. +The USRP X3x0, NI USRP-294x and NI USRP-295x devices <b>cannot</b> be hot-swapped when connected over PCI Express. +Unplugging the PCI Express connection or powering the device should be done only after disabling the device or +powering off the host computer. -<b>WARNING:</b> If the device is unplugged without running the above command, the -system could become unstable. +<b>WARNING:</b> If the device is unplugged without disabling it in device manager, the system could become unstable. \subsection win_uninstall Uninstalling NI-USRP in Windows diff --git a/host/docs/octoclock.dox b/host/docs/octoclock.dox index 7469e0719..362ee779f 100644 --- a/host/docs/octoclock.dox +++ b/host/docs/octoclock.dox @@ -7,41 +7,24 @@ - Hardware Capabilities: - Fully integrated timing source with 8-Way distribution (10 MHz and 1 PPS) - User selection between internal GPSDO (when present) or external 10 MHz/1 PPS source + - Ethernet bootloader for easy firmware upgrade - Source detection with automatic switch over in case of failure or disconnect - Streaming GPS time and NMEA strings over Ethernet (OctoClock-G only) \section octoclock_load Loading Firmware onto the Octoclock -\subsection bootloader OctoClock bootloader - -If you purchased your OctoClock device before Ethernet functionality was introduced, or if your unit's -bootloader has somehow become corrupted, you must burn the bootloader onto the device before you can load -the primary firmware. - -To load the bootloader onto the OctoClock, two things are needed: - -- AVR programmer -- AVRdude software - -Connect the AVR programmer to J108, as specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf"> -schematics</a>. Once you verify that the programmer is properly connected, run the following commands to burn the firmware: +First, the OctoClock's bootloader needs to be loaded onto the device. Connect the AVR programmer to J108, as +specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf">schematics</a>. Once you +verify that the programmer is properly connected, run the following commands to load the bootloader: cd <install path>/share/uhd/images - avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i + avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i + +When the bootloader is loaded, it will have a default IP address of `192.168.10.3`. \b Note: On Linux, `sudo avrdude ...` might be necessary to gain access to the programmer. -Once the bootloader has been burned, power-cycle your OctoClock device and refer to the below instructions on burning the OctoClock's -primary firmware. - -\subsection application Primary Octoclock firmware - -To load firmware onto the OctoClock, you must use the `octoclock_firmware_burner` utility, specifying the IP -address of the OctoClock device, as follows: - - octoclock_firmware_burner --addr=192.168.10.3 - \section octoclock_network Setting Up Networking \subsection host_interface Setting up the host interface diff --git a/host/docs/uhd.dox b/host/docs/uhd.dox index 949c710b1..fcd0a25b0 100644 --- a/host/docs/uhd.dox +++ b/host/docs/uhd.dox @@ -12,6 +12,7 @@ Some additional pages on developing UHD are also available here: \li \subpage page_coding \li \subpage page_converters \li \subpage page_stream +\li \subpage page_rtp */ // vim:ft=doxygen: diff --git a/host/docs/uhd_image_loader.1 b/host/docs/uhd_image_loader.1 new file mode 100644 index 000000000..5d5c88ec5 --- /dev/null +++ b/host/docs/uhd_image_loader.1 @@ -0,0 +1,130 @@ +.TH "uhd_image_loader" 1 "3.9.0" UHD "User Commands" +.SH NAME +uhd_image_loader - UHD Image Loader + +.SH DESCRIPTION +Burn firmware and FPGA images onto connected Ettus Research devices. + +.SH SYNOPSIS +.B uhd_image_loader [OPTIONS] + +.SH OPTIONS +.IP "List options:" +--help +.IP "Device and loader arguments:" +--args="" +.IP "Custom firmware filepath:" +--fw-path="" +.IP "Custom FPGA filepath:" +--fpga-path="" +.IP "Don't burn firmware:" +--no-fw +.IP "Don't burn FPGA:" +--no-fpga + +.SH SPECIFYING A PARTICULAR DEVICE +.sp +Without any arguments given through the \fIargs=""\fR option, this utility will start a session +with the first Ettus Research device it sees. The arguments shown below will narrow down the +utility's search for a particular device: + +.SS All devices +.sp +The \fIname=\fR and \fIserial=\fR arguments can be used to specify any device except an +uninitialized USB device or an OctoClock bootloader. The \fItype=\fR argument can be used +to specify any device, as described below: + +"type=" argument + +Argument | Device + +type=usrp2 | USRP N200, USRP N210 + +type=b200 | USRP B200, USRP B210 + +type=e100 | USRP E100, USRP E110 + +type=e3x0 | USRP E310 + +type=octoclock | OctoClock + +.sp +NOTE: The USRP1, USRP2, and USRP B100 are not supported. + +.SS Network devices +.sp +By default, this utility will check all network interfaces for network-based devices, but a specific +IP address can be specified with the \fIaddr=\fR argument. + +.SS NI-RIO devices +.sp +An X-Series devices connected via PCIe can be specified by its NI-RIO resource through the \fIresource=\fR +argument, and the RPC port through which to communicate with it can be specified with the \fIrpc-port=\fR +argument. Using these options is not recommended, as their default values are almost always used. + +.SS OctoClock devices +An OctoClock's name and serial are only exposed when the firmware is loaded, so if the device only has a +bootloader, only the \fIaddr=\fR argument can be used to find it. + +.SH DEVICE-SPECIFIC LOADER OPTIONS +.sp +Certain devices have specific options for customizing their image loading process, and these can be passed +in through the \fI--args=""\fR option. These arguments are specified below: + +.SS USRP N200, USRP N210 +.sp +The \fIoverwrite-safe\fR option will overwrite the device's safe-mode firmware and FPGA images. This is +NOT RECOMMENDED, as these images serve as the backup if the device's primary images are corrupted. + +.sp +The \fIreset\fR option will automatically reset the device once the loading process finishes. When the +device resets, it will have its new images loaded. + +.SS USRP X300, USRP X310 (Ethernet only) +.sp +The \fIconfigure\fR option will automatically reset the device once the loading process finishes. When +the device resets, it will have its new FPGA image loaded. + +.sp +The \fIverify\fR option will tell the device to internally verify the integrity of the image as it loads. +This greatly increases the loading time. + +.SH EXAMPLES + +.SS Load only the default FPGA image onto a specific N2x0 device and reset +.sp +uhd_image_loader --args="type=usrp2,addr=192.168.10.2,reset" --no-fw +.ft + +.SS Load a custom FPGA image onto a specific X3x0 device +.sp +uhd_image_loader --args="type=x300,addr=192.168.40.2" --fpga-path="/home/user/my_x300_fpga_image.bit" +.ft + +.fi + +.SH SEE ALSO +UHD documentation: +.B http://files.ettus.com/manual/ +.LP +GR-UHD documentation: +.B http://gnuradio.org/doc/doxygen/page_uhd.html +.LP +Other UHD programs: +.sp +uhd_images_downloader(1) usrp2_card_burner(1) +.SH AUTHOR +This manual page was written by Nicholas Corgan +for the Debian project (but may be used by others). +.SH COPYRIGHT +Copyright (c) 2015 Ettus Research LLC +.LP +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 +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. +.LP +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. diff --git a/host/docs/usrp2.dox b/host/docs/usrp2.dox index 7b6cb9ed0..3f85e45b5 100644 --- a/host/docs/usrp2.dox +++ b/host/docs/usrp2.dox @@ -62,32 +62,21 @@ both the FPGA and firmware images before power cycling. This ensures that when the device reboots, it has a compatible set of images to boot into. -\subsection usrp2_loadflash_netburner Use the net burner tool +\subsection usrp2_loadflash_imageloader Use the image loader Use default images: - usrp_n2xx_simple_net_burner --addr=<IP address> + uhd_image_loader --args="type=usrp2,addr=<IP address>" Use custom-built images: - usrp_n2xx_simple_net_burner --addr=<IP address> --fw=<firmware path> --fpga=<FPGA path> + uhd_image_loader --args="type=usrp2,addr=<IP address>" --fw-path="<firmware path>" --fpga-path="<FPGA path>" <b>Note:</b> Different hardware revisions require different FPGA images. Determine the revision number from the sticker on the rear of the chassis. Use this number to select the correct FPGA image for your device. -For users who would prefer a graphical utility, a Python-based -alternative exists. - -\subsection usrp2_loadflash_gui Use the graphical net burner tool (Linux) - - <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py - -\subsection usrp2_loadflash_guiwin Use the graphical net burner tool (Windows) - - <path_to_python.exe> <install-path>/lib/uhd/utils/usrp_n2xx_net_burner_gui.py - \subsection usrp2_loadflash_brick Device recovery and bricking Its possible to put the device into an unusable state by loading bad @@ -438,22 +427,20 @@ There is a sub-directory in the archive below the firmware/images called 'bit'. The USRP should now be able to communicate on the network (you'll see some LEDs light up and network link be established). The next step is to flash the device and program the serial number. Both these steps can be done with UHD (the JTAG step is complete). -To be sure, run `uhd_find_devices` and it should appear in the list - remember this IP address for the burner utility (should be 192.168.10.2 - make sure your network settings enable to you communicate with that subnet!). +To be sure, run `uhd_find_devices` and it should appear in the list - remember this IP address for the image loader utility (should be 192.168.10.2 - make sure your network settings enable to you communicate with that subnet!). -The first step is to flash the unit's safe-mode image, and then do a normal flash - both with the USRP N-series image burner utility. +The first step is to flash the unit's safe-mode image, and then do a normal flash - both with the UHD Image Loader utility. Make sure you have UHD installed, and the images from before, and follow the instructions in \ref usrp2_load. -You can combine the `--fw` and `--fpga` arguments into the single invocation of the burner. +You can combine the `--fw-path` and `--fpga-path` arguments into the single invocation of the image loader. You will probably use "usrp_n210_fw.bin" for the firmware and "usrp_n210_r4_fpga.bin" for the FPGA image parameters (use the full/relative file path if your current directory is not that of the images). - usrp_n2xx_net_burner.py --addr=192.168.10.2 --fw=usrp_n210_fw.bin --fpga=usrp_n210_r4_fpga.bin --overwrite-safe + uhd_image_loader --args="type=usrp2,addr=192.168.10.2,overwrite-safe" --fw-path=usrp_n210_fw.bin --fpga-path=usrp_n210_r4_fpga.bin -Use `--overwrite-safe` the first time, and then repeat without it for the second time. +Use the `overwrite-safe` option the first time, and then repeat without it for the second time. Don't forget to power-cycle the device after it has been flashed. -If you see a Python exception thrown (e.g. KeyError 65535) and something about if(check_rev)... use the --dont-check-rev option too (this is when the EEPROM has not yet been initialised, or has been blanked). - You can change the normal IP address by following the instructions in \ref usrp2_network_changeip. If you run `uhd_usrp_probe`, you can see the EEPROM keys at the top. Example: diff --git a/host/docs/usrp_b200.dox b/host/docs/usrp_b200.dox index bd79c4470..6c4e52dd6 100644 --- a/host/docs/usrp_b200.dox +++ b/host/docs/usrp_b200.dox @@ -2,7 +2,7 @@ \tableofcontents -\section b200_features Comparative features list - B200/B210 +\section b200_features Comparative features list - B200/B210/B200mini - Hardware Capabilities: - Integrated RF frontend (70 MHz - 6 GHz) @@ -10,16 +10,20 @@ - External 10 MHz reference input - Configurable clock rate - Variable analog bandwidth (200 kHz - 56 MHz) - - Internal GPSDO option (see \subpage page_gpsdo_b2x0 for details) - - B210 Only: - - MICTOR Debug Connector - - JTAG Connector - - Revision 6 with GPIO header + - GPIO header + - [B200/B210] Internal GPSDO option (see \subpage page_gpsdo_b2x0 for details) + - [B210/B200mini] JTAG Connector + - [B210] MICTOR Debug Connector - FPGA Capabilities: - Timed commands in FPGA - Timed sampling in FPGA -\section b200_imgs Specify a Non-standard Image +\section b200_power Power +In most cases, USB 3.0 bus power will be sufficient to power the device. +If using USB 2.0 or a internal GPSDO, an external power supply or a cable designed +to pull power from 2 USB ports (USB 3.0 dual A to micro-B or B) must be used. + +\section b200_imgs Specifying a Non-standard Image UHD software will automatically select the USRP B2X0 images from the installed images package. The image selection can be overridden with the @@ -38,15 +42,40 @@ images: The master clock rate feeds the RF frontends and the DSP chains. Users may select non-default clock rates to acheive integer decimations or -interpolations in the DSP chains. The default master clock rate defaults -to 32 MHz, but can be set to any rate between 5 MHz and 61.44 MHz. +interpolations in the DSP chains. The clock rate can be set to any value +between 5 MHz and 61.44 MHz (or 30.72 MHz for dual-channel mode). +Note that rates above 56 MHz are possible, but not recommended. The user can set the master clock rate through the usrp API call uhd::usrp::multi_usrp::set_master_clock_rate(), or the clock rate can be set through the -device arguments, which many applications take: : +device arguments, which many applications take: uhd_usrp_probe --args="master_clock_rate=52e6" +The property to control the master clock rate is a double value, called `tick_rate`. + +\subsection b200_auto_mcr Automatic Clock Rate Setting + +The default clock rate setting is to automatically set a clock rate +depending on the requested sampling rate. The automatic clock rate selection +is disabled when either `master_clock_rate` is given in the device initialization +arguments, or when uhd::usrp::multi_usrp::set_master_clock_rate() is called. + +Note that the master clock rate must be an integer multiple of the sampling +rate. If a master clock rate is chosen for which this condition does not +hold, a warning will be displayed and a different sampling rate is used internally. + +Nevertheless, there are multiple valid values for the master clock rate +for most sampling rates. The auto clock rate selection attempts to use +the largest possible clock rate as to enable as many half-band filters +as possible. Expert users might have cases where a more fine-grained +control over the resampling stages is required, in which case manually +selecting a master clock rate might be more suitable than the automatic +rate. + +The property to dis- or enable the auto tick rate is a boolean value, +`auto_tick_rate`. + \section b200_fe RF Frontend Notes The B200 features an integrated RF frontend. @@ -54,7 +83,7 @@ The B200 features an integrated RF frontend. \subsection b200_fe_tuning Frontend tuning The RF frontend has individually tunable receive and transmit chains. On -the B200, there is one transmit and one receive RF frontend. On the +the B200 and B200 mini, there is one transmit and one receive RF frontend. On the B210, both transmit and receive can be used in a MIMO configuration. For the MIMO case, both receive frontends share the RX LO, and both transmit frontends share the TX LO. Each LO is tunable between 50 MHz and 6 GHz. @@ -89,7 +118,7 @@ rate. \subsection LED Indicators -Below is a table of the LED indicators and their meanings: +Below is a table of the B200/B210 LED indicators and their meanings: <table> <tr> @@ -125,6 +154,36 @@ Below is a table of the LED indicators and their meanings: </tr> </table> +Below is a table of the B200mini LED indicators and their meanings: + +<table> + <tr> + <th>Component ID</th><th>Description</th><th>Details</th> + </tr> + <tr> + <td>PWR LED</td> <td>Power Indicator</td> <td>off = no power applied<br> + on = power applied (external or USB)</td> + </tr> + <tr> + <td>TRX LED</td> <td>TX/RX Activity</td> <td>off = no activity<br> + green = receiving<br> + red = transmitting<br> + orange = switching between transmitting and receiving</td> + </tr> + <tr> + <td>RX2 LED</td> <td>RX2 Activity</td> <td>off = no activity<br> + green = receiving</td> + </tr> + <tr> + <td>S0 LED</td> <td>Reference Lock</td> <td>off = no activity + green = locked</td> + </tr> + <tr> + <td>S1 LED</td> <td>Reference Present</td> <td>off = reference level low or not present<br> + green = reference level high</td> + </tr> +</table> + TX LED indicators are on when transimitting data and off when no samples are available to transmit. RX LED indicators are on when sending samples to the host and off when unable to do so. This means that TX/RX activity LED @@ -135,7 +194,7 @@ well. \subsection External Connections -Below is a table showing the external connections and respective power information: +Below is a table showing the B200/B210 external connections and respective power information: <table> <tr> @@ -172,15 +231,36 @@ Below is a table showing the external connections and respective power informati </tr> </table> +Below is a table showing the B200mini external connections and respective power information: + +<table> +<tr> + <th>Component ID</th> <th>Description</th> <th> Details</th> +</tr> +<tr> + <td>USB3</td> <td>USB Connector</td> <td>USB 3.0</td> +</tr> +<tr> + <td>J1</td> <td>TRX</td> <td>TX power +20dBm max<br> + RX power -15dBm max</td> +</tr> +<tr> + <td>J2</td> <td>RX2</td> <td>RX power -15dBm max</td> +</tr> +<tr> + <td>J3</td> <td>External 10MHz/PPS Reference</td> <td>+15 dBm max</td +</tr> +</table> + \subsection b200_switches On-Board Connectors and Switches -Below is a table showing the on-board connectors and switches: +Below is a table showing the B200/B210 on-board connectors and switches: Component ID | Description | Details ------------------------|----------------------------|--------------------------------------------------- J502<sup>1</sup> | Mictor Connector | Interface to FPGA for I/O and inspection. J503<sup>1</sup> | JTAG Header | Interface to FPGA for programming and debugging. - J504<sup>2</sup> | GPIO Header | Header running to the FPGA for GPIO purposes. + J504<sup>2</sup> | GPIO Header | Header connected to the FPGA for GPIO purposes. S700 | FX3 Hard Reset Switch | Resets the USB controller / System reset U100 | GPSDO socket | Interface to GPS disciplined reference oscillator @@ -188,6 +268,16 @@ Component ID | Description | Details <sup>2</sup> Only since rev. 6 (green board) +Below is a table showing the B200mini on-board connectors and switches: + +Component ID | Description | Details +------------------------|----------------------------|--------------------------------------------------- + J5 | JTAG Header | Interface to FPGA for programming and debugging. + J6<sup>1</sup> | GPIO Header | Header connected to the FPGA for GPIO purposes. + SW1 | FX3 Hard Reset Switch | Resets the USB controller / System reset + + <sup>1</sup> GPIO pinout is 1=3.3V, 2=GPIO_0, 3=GPIO_1, 4=GPIO_2, 5=GPIO_3, 6=GND, 7=3.3V, 8=GPIO_4, 9=GPIO_5, 10=,GPIO_6 11=GPIO_7, 12=GND + */ // vim:ft=doxygen: diff --git a/host/docs/usrp_e3x0.dox b/host/docs/usrp_e3x0.dox index ac9687f4f..db4114360 100644 --- a/host/docs/usrp_e3x0.dox +++ b/host/docs/usrp_e3x0.dox @@ -134,6 +134,8 @@ which should return 'arm-oe-linux-gnueabi'. $ cmake -DCMAKE_TOOLCHAIN_FILE=<youruhdsrc>/host/cmake/Toolchains/oe-sdk_cross.cmake -DCMAKE_INSTALL_PREFIX=/usr -DENABLE_E300=ON .. $ make +For instructions on building UHD on a PC to interact with your E-Series device, follow these instructions: \ref e3x0_uhd_build + \subsubsection e3x0_sdk_usage_gnuradio Building GNU Radio -# Obtain the gnuradio source code via git. @@ -188,9 +190,9 @@ builds) $ export MACHINE="ettus-e300" $ bitbake gnuradio-dev-image \endcode -When this completes, the files needed to create the sd card are in -`tmp-glibc/deploy/images/ettus-e300`. See \ref e3x0_upgrade_sd_card for instructions to write the image to your sd card. +When this completes, the files needed to create the SD card are in +`tmp-glibc/deploy/images/ettus-e300` -# Build the toolchain. \code{.sh} @@ -693,16 +695,28 @@ usrp->set_rx_subdev_spec("A:A A:B"); The following sensors are available for the USRP-E Series motherboards; they can be queried through the API. -- **fe_locked** - rx / tx frontend pll locked +- **fe_locked** - rx / tx frontend PLL locked - **temp** - processor temperature value - **gps_time** and **gps_locked** sensors are added when the GPS is found \subsection e3x0_network_mode Network Mode -Your USRP-E series device can be used in network mode for narrow band signal observation, evaluation and debugging purposes. +Your USRP-E series device can be used in network mode for narrow band signal observation, evaluation and debugging purposes. See the instructions below for how to use network mode. Please note that when compared with normal operation as a standalone device the usable bandwidth is limited and therefore Network Mode is not the recommended mode of operation. +\subsubsection e3x0_uhd_build Building UHD + +To work with your E-Series device in network mode, you will need to build UHD on your PC with extra CMake flags. Assuming you are in the host/build directory, +see below: + + $ cmake -DENABLE_E300=ON -DE300_FORCE_NETWORK=ON .. + $ make + +Once UHD is installed on your device, it will be able to interact with an E-Series device with network mode active (see below). + +\subsubsection e3x0_activating_network Activating Network Mode on the Device + In order to use the device in network mode it is necessary to start the *usrp_e3x0_network_mode* executable on the device. In order to start the executable please log into your device either via SSH or serial console(see \ref e3x0_first_boot) and type diff --git a/host/docs/usrp_x3x0.dox b/host/docs/usrp_x3x0.dox index d9657424e..766766c21 100644 --- a/host/docs/usrp_x3x0.dox +++ b/host/docs/usrp_x3x0.dox @@ -89,13 +89,13 @@ number, you will have to update the FPGA image before you can start using your U 1. Download the current UHD images. You can use the `uhd_images_downloader` script provided with UHD (see also \ref page_images). -2. Use the `usrp_x3xx_fpga_burner` utility to update the FPGA image. On the command line, run: +2. Use the `uhd_image_loader` utility to update the FPGA image. On the command line, run: - usrp_x3xx_fpga_burner --addr=192.168.10.2 --type=HGS + uhd_image_loader --args="type=x300,addr=192.168.10.2,fpga=HGS" If you have installed the images to a non-standard location, you might need to run (change the filename according to your device): - usrp_x3xx_fpga_burner --addr=192.168.10.2 --fpga-path <path_to_images>/usrp_x310_fpga_HGS.bit + uhd_image_loader --args="type=x300,addr=192.168.10.2" --fpga-path="<path_to_images>/usrp_x310_fpga_HGS.bit" The process of updating the FPGA image will take several minutes. Make sure the process of flashing the image does not get interrupted. @@ -303,31 +303,31 @@ detect your device information, and you will need to use this number to select which image to burn. \b Note: -The burner utility will default to using the appropriate BIT file if no custom +The image loader utility will default to using the appropriate BIT file if no custom FPGA image path is specified, but it is compatible with BIN, BIT, and LVBITX images. -\subsection x3x0_flash_burner_tool Use the burner tool over Ethernet +\subsection uhd_image_loader_tool Use the image loader over Ethernet Automatic FPGA path, detect image type: - usrp_x3xx_fpga_burner --addr=<IP address> + uhd_image_loader --args="type=x300,addr=<IP address>" Automatic FPGA path, select image type: - usrp_x3xx_fpga_burner --addr=<IP address> --type=<HGS or XGS> + uhd_image_loader --args="type=x300,addr=<IP address>,fpga=<HGS or XGS>" Manual FPGA path: - usrp_x3xx_fpga_burner --addr=<IP address> --fpga-path=<path to FPGA image> + uhd_image_loader --args="type=x300,addr=<IP address>" --fpga-path="<path to FPGA image>" -\subsection x3x0_flash_burner_tool_pcie Use the burner tool over PCI Express +\subsection uhd_image_loader_tool_pcie Use the image loader over PCI Express Automatic FPGA path, detect image type: - usrp_x3xx_fpga_burner --resource=<NI-RIO resource> + uhd_image_loader --args="type=x300,resource=<NI-RIO resource>" Automatic FPGA path, select image type: - usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --type=<HGS or XGS> + uhd_image_loader --args="type=x300,resource=<NI-RIO resource>,fpga=<HGS or XGS>" Manual FPGA path: - usrp_x3xx_fpga_burner --resource=<NI-RIO resource> --fpga-path=<path to FPGA image> + uhd_image_loader --args="type=x300,resource=<NI-RIO resource>" --fpga-path="<path to FPGA image>" \subsection x3x0_flash_bricking Device recovery and bricking It is possible to put the device into an unusable state by loading bad images ("bricking"). @@ -480,7 +480,7 @@ and that it is using the expected IP address. ping 192.168.10.2 -\subsection x3x0_comm_problems_not_enumerated USRP device not enumerated (Linux) +\subsection x3x0_comm_problems_not_enumerated Device not enumerated over PCI-Express (Linux) UHD requires the RIO device manager service to be running in order to communicate with an X-Series USRP over PCIe. This service is installed as @@ -493,19 +493,24 @@ run the following command: If the device still does not enumerate after starting the device manager, make sure that the host computer has successfully detected it. You can do so by running the following command: - lspci -k -d 1093:c4c4 + lspci -k -d 1093:c4c4 A device similar to the following should be detected: - $ lspci -k -d 1093:c4c4 - 04:00.0 Signal processing controller: National Instruments ... + $ lspci -k -d 1093:c4c4 + 04:00.0 Signal processing controller: National Instruments ... Subsystem: National Instruments Device 76ca Kernel driver in use: niusrpriok_shipped -- A USRP X300 should appear with 'Subsystem: National Instruments Device 7736' -- A USRP X310 should appear with 'Subsystem: National Instruments Device 76ca' +- All USRP X-Series devices should appear with 'Subsystem: National Instruments Device' +- The device ID following can be: + - USRP X300: 7736 or 7861 + - USRP X310: 76CA or 7862 + - NI-USRP 294xR: 772B, 77FB, 772C, 77FC, 772D, 77FD, 772E, 7853, 785B, 7854, 785C, 7855, 785D or 7856 + - NI-USRP 295xR: 772F, 77FE, 7730, 77FF, 7731, 7800, 7732, 7857, 785E, 7858, 785F, 7859, 7860 or 785A -\subsection x3x0_comm_problems_not_enumerated_win USRP device not enumerated (Windows) + +\subsection x3x0_comm_problems_not_enumerated_win Device not enumerated over PCI-Express (Windows) UHD requires the RIO device manager service to be running in order to communicate with an X-Series USRP over PCIe. diff --git a/host/docs/usrp_x3x0_config.dox b/host/docs/usrp_x3x0_config.dox index 2ee449cc2..adada7623 100644 --- a/host/docs/usrp_x3x0_config.dox +++ b/host/docs/usrp_x3x0_config.dox @@ -133,134 +133,19 @@ The above file was generated and modified on a Fedora 20 system. \subsection x3x0cfg_hostpc_pcie PCI Express Configuration +\ref page_ni_rio_kernel + \b Note: These instructions are \b only relevant for using a USRP X3xx device over a cabled PCIe transport (PCIe over MXIe cable). If you are using your USRP X3xx device over 1 Gigabit Ethernet or 10 Gigabit Ethernet, these instructions do <em>not apply to you.</em> -\subsubsection x3x0cfg_hostpc_pcie_linux Linux Systems - In order to use the USRP X300/X310 with PCI Express on Linux, NI USRP RIO kernel modules needs to be installed. In order to use the PCIe kernel module, and thus PCIe-over-cable with the X3xx, -your kernel version must be supported by the kernel module. -Only 64-bit kernels are supported. To check if your kernel is supported -by the drivers, check the NI USRP RIO website. - -#### Downloading and Extracting Installer - -- Download the installer from here: http://files.ettus.com/binaries/niusrprio/niusrprio-installer.tar.gz -- Extract it with the following command: - - tar zxf niusrprio-installer.tar.gz - -The files will be extracted into a directory called `niusrprio-installer`. - -#### Installation - -To install the NI USRPRIO kernel modules and RPC server, run the following command: - - sudo niusrprio-installer/INSTALL - -Select `y` for each prompt, and the script will install all necessary components. -This script will automatically load all necessary kernel modules for -the duration of the session. - -#### Enabling/Disabling Usage - -Once everything is installed, run the following commands to enable -use of the X300/X310 over PCI Express (the assumption is that `niusrprio_pcie` -was installed into your `$PATH`): - - sudo niusrprio_pcie start - -To stop these processes, run the following command: - - sudo niusrprio_pcie stop - -To check if the kernel modules are loaded and if the RPC server -is running, run the following command: - - niusrprio_pcie status - -\b Note: `niusrprio_pcie` start does not run when the host system is booted. -If you would like the USRP PCIe device to be available automatically after -a system restart, please create an init.d script that runs `niusrprio_pcie start`. - -#### Hot-plugging/Power-cycling - -The USRP X3x0, NI USRP-294x and NI USRP-295x devices cannot be hot-swapped when -connected over PCI Express. Unplugging the PCI Express connection or powering -the device should be done only after disabling the device using the following command. - - sudo /usr/local/bin/niusrprio_pcie stop - -\b Warning: If the device is unplugged without running the above command, the system -can become unstable and crash. - -#### Linux kernel upgrades - -The NI USRP RIO kernel modules are built for a specific kernel version. -If you upgrade/downgrade the linux kernel on the host to a version different -from the one that the installer was run on, then you may see the following -error message when running `niusrprio start`: - - ERROR: could not insert 'NiRioSrv': Unknown symbol in module, or unknown parameter (see dmesg) - ERROR: could not insert 'niusrpriok': Unknown symbol in module, or unknown parameter (see dmesg) - -To rebuild the kernel modules for the currently running kernel, simple run the following - - sudo /usr/local/bin/niusrprio_pcie stop - sudo /usr/local/bin/updateNIDrivers --no-prompt - sudo /usr/local/bin/niusrprio_pcie start - -#### Uninstallation +your operating and kernel must be supported by the drivers. Please look +at the page above for a list of supported OS/kernel versions. -To uninstall the NI USRP RIO kernel modules and RPC server, run the following command: - - sudo niusrprio-installer/UNINSTALL - -Select `y` at the prompt, and the script will uninstall all installed components. - -\subsubsection x3x0cfg_hostpc_pcie_windows Windows - -In order to use the USRP X300/X310 with PCI Express on Windows, the -NI-RIO driver package and the NI-USRP RIO kernel driver needs to be installed. - -\b Note: The kernel driver is only supported with certain Windows versions. -Check the NI USRP RIO website to see if your version is supported. - -#### Installing NI-USRP - -The NI-USRP 1.3 installer can be downloaded from this location: http://www.ni.com/download/ni-usrp-1.3/4711/en/ - -You will need to create a free NI User Account to download the installer. -Perform the following steps to download and install the NI-USRP exe driver package: - -- Choose the "2. Standard Download:NIUSRP130.exe" option to download NIUSRP130.exe to your computer -- Run NIUSRP130.exe as an Administrator and extract the contents to C:\\National Instruments Downloads\\NI-USRP\\1.3 -- In the extract location, run setup.exe and follow the prompts. - -Reboot the computer after both the NI-USRP package has been installed. - -#### Enabling/Disabling Usage - -Once everything is installed and the system is rebooted, your X300/X310 PCI Express device should automatically be detected by the Windows Device Manager. The device should be enabled by default. - -- To disable the USRPRIO device, navigate to "Device Manager", locate your USRPRIO-X3x0 device, right-click on it and choose "Disable". -- To enable the USRPRIO device, navigate to "Device Manager", locate your USRPRIO-X3x0 device, right-click on it and choose "Enable". - -#### Hot-plugging/Power-cycling - -The USRP X3x0, NI USRP-294x and NI USRP-295x devices cannot be hot-swapped when connected over PCI Express. Unplugging the PCI Express connection or powering the device should be done only after disabling the device. - -\b Warning: If the device is unplugged without running the above command, the system can become unstable and crash - -#### Uninstalling NI-USRP - -Navigate to the Control Panel and open "Programs". Then select National Instruments -Software and select NI-USRP and NI-RIO from the list. Click on uninstall -to remove the drivers from your system. \subsection x3x0cfg_hostpc_pwr Power Management @@ -282,20 +167,22 @@ issues, set the governor to "performance". 1. Install cpufrequtils: - sudo apt-get install cpufrequtils + `sudo apt-get install cpufrequtils` 2. Edit `/etc/init.d/cpufrequtils` and set `GOVERNOR="performance"` on the appropriate line (run as root): - sed s/^GOVERNOR=.*$/GOVERNOR=\"performance\"/g /etc/init.d/cpufrequtils > /etc/init.d/cpufrequtils + `sed s/^GOVERNOR=.*$/GOVERNOR=\"performance\"/g /etc/init.d/cpufrequtils > /etc/init.d/cpufrequtils` 3. Restart cpufrequtils: - sudo /etc/init.d/cpufrequtils restart + `sudo /etc/init.d/cpufrequtils restart` \b Fedora: - sudo cpupower frequency-set -g performance +1. Change the CPU governor: + + `sudo cpupower frequency-set -g performance` \subsection x3x0cfg_hostpc_rtprio Real-Time & Priority Scheduling @@ -320,38 +207,12 @@ Real-time scheduling is enabled via different methods depending on your application and operating system. In GNU Radio Companion, it can be turned on in each individual flowgraph. -\subsection x3x0cfg_hostpc_volk Building with ORC & Volk +\subsection x3x0cfg_hostpc_volk SIMD Acceleration Especially when running high-performance applications, processing -performance can be dramatically improved by SIMD instructions. UHD uses -ORC to provide SIMD capability, and GNU Radio includes a SIMD library -called "Volk". These should both be used to guarantee optimum -performance. - -\subsubsection x3x0cfg_hostpc_volk_orc Compiling UHD with ORC - -ORC, the <a href="http://code.entropywave.com/orc/">Oil Runtime Compiler</a>, -is a third-party compiler that UHD uses to create efficient SIMD code for -your particular computer. ORC is generally easily installed from your -OS's package manager. - -On Fedora: - - $ sudo yum update; sudo yum install orc-compiler orc-devel - -On Ubuntu: - - $ sudo apt-get update; sudo apt-get install liborc-<version> liborc-<version>-dev - -After installing ORC, when building UHD from source, you should see -"ORC" as one of the configured UHD components. - - -- ###################################################### - -- # UHD enabled components - -- ###################################################### - -- * LibUHD - <cut for brevity> - -- * ORC +performance can be dramatically improved by SIMD instructions. +GNU Radio includes a SIMD library +called "Volk", which should be used to guarantee optimum performance. \subsubsection x3x0cfg_hostpc_volk_volk Compiling GNURadio with Volk diff --git a/host/docs/vrt_chdr.dox b/host/docs/vrt_chdr.dox new file mode 100644 index 000000000..8ab177b21 --- /dev/null +++ b/host/docs/vrt_chdr.dox @@ -0,0 +1,83 @@ +/*! \page page_rtp Radio Transport Protocols + +\tableofcontents + +Radio transport protocols are used to exchange samples (or other items) between host and devices. +If one were to sniff Ethernet traffic between a USRP and a PC, the packets would conform to a +radio transport protocol. + +For USRP devices, two radio transport protocols are relevent: VRT (the VITA Radio Transport protocol) +and CVITA (compressed VITA), also known as CHDR. Generation-3 devices and the B200 use CHDR, the rest +use VRT. + +\section rtp_vrt VRT + +VRT is an open protocol defined by the VITA-49 standard. It was designed for interoperability, +and to allow different device types to work with different software stacks. + +VRT is a very verbose standard, and only a subset is implemented in UHD/USRPs. +The full standard is available from the VITA website: http://www.vita.com . + + +\section rtp_chdr CVITA (CHDR) + +For the third generation of Ettus devices, a new type transport protocol was designed. +It reduces the complexity of the original standard and uses a fixed-length 64-Bit header +for everything except the timestamp. Because this is a "compressed" form of VITA, it +was dubbed "Compressed VITA" (CVITA). The compressed header is called CHDR, which is why +the protocol is often called CHDR itself (pronounced like the cheese "cheddar"). + +By compressing all information into a 64-bit line, the header can efficiently be parsed +in newer FPGAs, where the common streaming protocol is 64-Bit AXI. The first line in a +packet already provides all necessary information to proceed. + +Some CHDR-specific functions can be found in: uhd::transport::vrt::chdr. + +The form of a CVITA packet is the following: + +Address (Bytes) | Length (Bytes) | Payload +----------------|----------------|---------------------------- +0 | 8 | Compressed Header (CHDR) +8 | 8 | Fractional Time (Optional!) +8/16 | - | Data + +If there is no timestamp present, the data starts at address 8, otherwise, it starts at 16. + +The 64 Bits in the compressed header have the following meaning: + +Bits | Meaning +-------|-------------------------------------------------- +63:62 | Packet Type +61 | Has fractional time stamp (1: Yes) +60 | End-of-burst or error flag +59:48 | 12-bit sequence number +47:32 | Total packet length in Bytes +31:0 | Stream ID (SID) + + +The packet type is determined mainly by the first two bits, although +the EOB or error flag are also taken into consideration: + +Bit 63 | Bit 62 | Bit 60 | Packet Type +-------|--------|--------|-------------- +0 | 0 | 0 | Data +0 | 0 | 1 | Data (End-of-burst) +0 | 1 | 0 | Flow Control +1 | 0 | 0 | Command Packet +1 | 1 | 0 | Command Response +1 | 1 | 1 | Command Response (Error) + +\section vrt_tools Tools + +For CHDR, we provide a Wireshark dissector under tools/chdr_dissector. It can be used +for Ethernet links as well as USB (e.g., for the B210). + +\section vrt_code Code + +Relevent code sections for the radio transport layer are: +* uhd::transport::vrt - Namespace for radio transport protocol related functions and definitions +* uhd::transport::vrt::chdr - Sub-namespace specifically for CVITA/CHDR +* uhd::sid_t - Datatype to represent SIDs + +*/ +// vim:ft=doxygen: diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 1e6f2f013..43e0db9c0 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2014 Ettus Research LLC +# Copyright 2010-2015 Ettus Research LLC # # 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 @@ -29,14 +29,13 @@ SET(example_sources test_messages.cpp test_pps_input.cpp test_timed_commands.cpp - transport_hammer.cpp tx_bursts.cpp tx_samples_from_file.cpp tx_timed_samples.cpp tx_waveforms.cpp txrx_loopback_to_file.cpp latency_test.cpp - fpgpio.cpp + gpio.cpp ) IF(ENABLE_OCTOCLOCK) @@ -62,3 +61,48 @@ IF(CURSES_FOUND) TARGET_LINK_LIBRARIES(rx_ascii_art_dft uhd ${CURSES_LIBRARIES} ${Boost_LIBRARIES}) UHD_INSTALL(TARGETS rx_ascii_art_dft RUNTIME DESTINATION ${PKG_LIB_DIR}/examples COMPONENT examples) ENDIF(CURSES_FOUND) + +######################################################################## +# Examples using C API +######################################################################## +IF(ENABLE_C_API) + # + # Check if this particular C99 feature is available with this compiler + # + INCLUDE(CheckCSourceCompiles) + CHECK_C_SOURCE_COMPILES(" + typedef struct { + int bar; + int baz; + } foo; + + int main() + { + foo wat = { + .bar = 1, + .baz = 2 + }; + + return 0; + } + " HAVE_C99_STRUCTDECL) + + IF(HAVE_C99_STRUCTDECL) + ADD_SUBDIRECTORY(getopt) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/getopt) + + SET(C_API_EXAMPLES + rx_samples_c + tx_samples_c + ) + + FOREACH(example ${C_API_EXAMPLES}) + ADD_EXECUTABLE(${example} ${example}.c) + TARGET_LINK_LIBRARIES(${example} uhd getopt) + IF(UNIX) + TARGET_LINK_LIBRARIES(${example} m) + ENDIF(UNIX) + UHD_INSTALL(TARGETS ${example} RUNTIME DESTINATION ${PKG_LIB_DIR}/examples COMPONENT examples) + ENDFOREACH(example ${C_API_EXAMPLES}) + ENDIF(HAVE_C99_STRUCTDECL) +ENDIF(ENABLE_C_API) diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index 03d8f3477..cc3ef04a4 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -43,7 +43,12 @@ unsigned long long num_seq_errors = 0; /*********************************************************************** * Benchmark RX Rate **********************************************************************/ -void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ +void benchmark_rx_rate( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &rx_cpu, + uhd::rx_streamer::sptr rx_stream, + bool random_nsamps +) { uhd::set_thread_priority_safe(); //print pre-test summary @@ -68,15 +73,19 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c rx_stream->issue_stream_cmd(cmd); while (not boost::this_thread::interruption_requested()){ + if (random_nsamps) { + cmd.num_samps = rand() % max_samps_per_packet; + rx_stream->issue_stream_cmd(cmd); + } try { - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); + num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels(); } catch (...) { - /* apparently, the boost thread interruption can sometimes result in - throwing exceptions not of type boost::exception, this catch allows - this thread to still attempt to issue the STREAM_MODE_STOP_CONTINUOUS - */ - break; + /* apparently, the boost thread interruption can sometimes result in + throwing exceptions not of type boost::exception, this catch allows + this thread to still attempt to issue the STREAM_MODE_STOP_CONTINUOUS + */ + break; } //handle the error codes @@ -109,7 +118,12 @@ void benchmark_rx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_c /*********************************************************************** * Benchmark TX Rate **********************************************************************/ -void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ +void benchmark_tx_rate( + uhd::usrp::multi_usrp::sptr usrp, + const std::string &tx_cpu, + uhd::tx_streamer::sptr tx_stream, + bool random_nsamps=false +) { uhd::set_thread_priority_safe(); //print pre-test summary @@ -127,9 +141,25 @@ void benchmark_tx_rate(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_c buffs.push_back(&buff.front()); //same buffer for each channel md.has_time_spec = (buffs.size() != 1); - while (not boost::this_thread::interruption_requested()){ - num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels();; - md.has_time_spec = false; + if (random_nsamps) { + std::srand( (unsigned int)time(NULL) ); + while(not boost::this_thread::interruption_requested()){ + size_t total_num_samps = rand() % max_samps_per_packet; + size_t num_acc_samps = 0; + const float timeout = 1; + + usrp->set_time_now(uhd::time_spec_t(0.0)); + while(num_acc_samps < total_num_samps){ + //send a single packet + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout)*tx_stream->get_num_channels(); + num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); + } + } + } else { + while (not boost::this_thread::interruption_requested()){ + num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md)*tx_stream->get_num_channels(); + md.has_time_spec = false; + } } //send a mini EOB packet @@ -182,6 +212,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string rx_cpu, tx_cpu; std::string mode; std::string channel_list; + bool random_nsamps = false; //setup the program options po::options_description desc("Allowed options"); @@ -196,6 +227,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") + ("random", "Run with random values of samples in send() and recv() to stress-test the I/O.") ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)") ; po::variables_map vm; @@ -213,6 +245,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ return ~0; } + // Random number of samples? + if (vm.count("random")) { + std::cout << "Using random number of samples in send() and recv() calls." << std::endl; + random_nsamps = true; + } + //create a usrp device std::cout << std::endl; uhd::device_addrs_t device_addrs = uhd::device::find(args, uhd::device::USRP); @@ -251,7 +289,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::stream_args_t stream_args(rx_cpu, rx_otw); stream_args.channels = channel_nums; uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream)); + thread_group.create_thread(boost::bind(&benchmark_rx_rate, usrp, rx_cpu, rx_stream, random_nsamps)); } //spawn the transmit test thread @@ -261,7 +299,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::stream_args_t stream_args(tx_cpu, tx_otw); stream_args.channels = channel_nums; uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); - thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream)); + thread_group.create_thread(boost::bind(&benchmark_tx_rate, usrp, tx_cpu, tx_stream, random_nsamps)); thread_group.create_thread(boost::bind(&benchmark_tx_rate_async_helper, tx_stream)); } @@ -287,6 +325,5 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //finished std::cout << std::endl << "Done!" << std::endl << std::endl; - return EXIT_SUCCESS; } diff --git a/host/examples/fpgpio.cpp b/host/examples/fpgpio.cpp deleted file mode 100644 index c57893669..000000000 --- a/host/examples/fpgpio.cpp +++ /dev/null @@ -1,418 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -// Example for front panel GPIO. -// Bits are set as follows: -// FPGPIO[0] = ATR output 1 at idle -// FPGPIO[1] = ATR output 1 during RX -// FPGPIO[2] = ATR output 1 during TX -// FPGPIO[3] = ATR output 1 during full duplex -// FPGPIO[4] = output -// FPGPIO[5] = input -// FPGPIO[6] = input (X series only) -// FPGPIO[7] = input (X series only) -// FPGPIO[8] = input (X series only) -// FPGPIO[9] = input (X series only) -// FPGPIO[10] = input (X series only) -// The example cycles through idle, TX, RX, and full duplex, spending 2 seconds for each. -// Outputs can be physically looped back to inputs for verification testing. - -#include <uhd/utils/thread_priority.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/multi_usrp.hpp> -#include <uhd/convert.hpp> -#include <boost/program_options.hpp> -#include <boost/format.hpp> -#include <boost/cstdint.hpp> -#include <boost/thread.hpp> -#include <csignal> -#include <iostream> - -static const std::string FPGPIO_DEFAULT_CPU_FORMAT = "fc32"; -static const std::string FPGPIO_DEFAULT_OTW_FORMAT = "sc16"; -static const double FPGPIO_DEFAULT_RX_RATE = 500e3; -static const double FPGPIO_DEFAULT_TX_RATE = 500e3; -static const double FPGPIO_DEFAULT_DWELL_TIME = 2.0; -static const std::string FPGPIO_DEFAULT_GPIO = "FP0"; -static const size_t FPGPIO_DEFAULT_NUM_BITS = 11; - -static UHD_INLINE boost::uint32_t FPGPIO_BIT(const size_t x) -{ - return (1 << x); -} - -namespace po = boost::program_options; - -static bool stop_signal_called = false; -void sig_int_handler(int){stop_signal_called = true;} - -std::string to_bit_string(boost::uint32_t val, const size_t num_bits) -{ - std::string out; - for (int i = num_bits - 1; i >= 0; i--) - { - std::string bit = ((val >> i) & 1) ? "1" : "0"; - out += " "; - out += bit; - } - return out; -} - -void output_reg_values( - const std::string bank, - const uhd::usrp::multi_usrp::sptr &usrp, - const size_t num_bits) -{ - std::cout << (boost::format("Bit ")); - for (int i = num_bits - 1; i >= 0; i--) - std::cout << (boost::format(" %s%d") % (i < 10 ? " " : "") % i); - std::cout << std::endl; - std::cout << "CTRL: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("CTRL"))), - num_bits) - << std::endl; - - std::cout << "DDR: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("DDR"))), - num_bits) - << std::endl; - - std::cout << "ATR_0X: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_0X"))), - num_bits) - << std::endl; - - std::cout << "ATR_RX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_RX"))), - num_bits) - << std::endl; - - std::cout << "ATR_TX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_TX"))), - num_bits) - << std::endl; - - std::cout << "ATR_XX: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("ATR_XX"))), - num_bits) - << std::endl; - - std::cout << "OUT: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("OUT"))), - num_bits) - << std::endl; - - std::cout << "READBACK: " << to_bit_string( - boost::uint32_t(usrp->get_gpio_attr(bank, std::string("READBACK"))), - num_bits) - << std::endl; -} - -int UHD_SAFE_MAIN(int argc, char *argv[]) -{ - uhd::set_thread_priority_safe(); - - //variables to be set by po - std::string args; - std::string cpu, otw; - double rx_rate, tx_rate, dwell; - std::string fpgpio; - size_t num_bits; - - //setup the program options - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") - ("repeat", "repeat loop until Ctrl-C is pressed") - ("cpu", po::value<std::string>(&cpu)->default_value(FPGPIO_DEFAULT_CPU_FORMAT), "cpu data format") - ("otw", po::value<std::string>(&otw)->default_value(FPGPIO_DEFAULT_OTW_FORMAT), "over the wire data format") - ("rx_rate", po::value<double>(&rx_rate)->default_value(FPGPIO_DEFAULT_RX_RATE), "rx sample rate") - ("tx_rate", po::value<double>(&tx_rate)->default_value(FPGPIO_DEFAULT_TX_RATE), "tx sample rate") - ("dwell", po::value<double>(&dwell)->default_value(FPGPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") - ("gpio", po::value<std::string>(&fpgpio)->default_value(FPGPIO_DEFAULT_GPIO), "name of gpio bank") - ("bits", po::value<size_t>(&num_bits)->default_value(FPGPIO_DEFAULT_NUM_BITS), "number of bits in gpio bank") - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - //print the help message - if (vm.count("help")){ - std::cout << boost::format("Front Panel GPIO %s") % desc << std::endl; - return ~0; - } - - //create a usrp device - std::cout << std::endl; - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - - //print out initial unconfigured state of FP GPIO - std::cout << "Unconfigured GPIO values:" << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - - //configure GPIO registers - boost::uint32_t ctrl = 0; // default all as manual - boost::uint32_t ddr = 0; // default all as input - boost::uint32_t atr_idle = 0; - boost::uint32_t atr_rx = 0; - boost::uint32_t atr_tx = 0; - boost::uint32_t atr_duplex = 0; - boost::uint32_t mask = 0x7ff; - - //set up FPGPIO outputs: - //FPGPIO[0] = ATR output 1 at idle - ctrl |= FPGPIO_BIT(0); - atr_idle |= FPGPIO_BIT(0); - ddr |= FPGPIO_BIT(0); - - //FPGPIO[1] = ATR output 1 during RX - ctrl |= FPGPIO_BIT(1); - ddr |= FPGPIO_BIT(1); - atr_rx |= FPGPIO_BIT(1); - - //FPGPIO[2] = ATR output 1 during TX - ctrl |= FPGPIO_BIT(2); - ddr |= FPGPIO_BIT(2); - atr_tx |= FPGPIO_BIT(2); - - //FPGPIO[3] = ATR output 1 during full duplex - ctrl |= FPGPIO_BIT(3); - ddr |= FPGPIO_BIT(3); - atr_duplex |= FPGPIO_BIT(3); - - //FPGPIO[4] = output - ddr |= FPGPIO_BIT(4); - - //set data direction register (DDR) - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); - - //set ATR registers - usrp->set_gpio_attr(fpgpio, std::string("ATR_0X"), atr_idle, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_RX"), atr_rx, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_TX"), atr_tx, mask); - usrp->set_gpio_attr(fpgpio, std::string("ATR_XX"), atr_duplex, mask); - - //set control register - usrp->set_gpio_attr(fpgpio, std::string("CTRL"), ctrl, mask); - - //print out initial state of FP GPIO - std::cout << "\nConfigured GPIO values:" << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - std::cout << std::endl; - - //set up streams - uhd::stream_args_t rx_args(cpu, otw); - uhd::stream_args_t tx_args(cpu, otw); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(rx_args); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(tx_args); - uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - rx_cmd.stream_now = true; - usrp->set_rx_rate(rx_rate); - usrp->set_tx_rate(tx_rate); - - //set up buffers for tx and rx - const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - const size_t nsamps_per_buff = max_samps_per_packet; - std::vector<char> rx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); - std::vector<char> tx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); - std::vector<void *> rx_buffs, tx_buffs; - for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) - rx_buffs.push_back(&rx_buff.front()); //same buffer for each channel - for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) - tx_buffs.push_back(&tx_buff.front()); //same buffer for each channel - - uhd::rx_metadata_t rx_md; - uhd::tx_metadata_t tx_md; - tx_md.has_time_spec = false; - tx_md.start_of_burst = true; - uhd::time_spec_t stop_time; - double timeout = 0.01; - uhd::time_spec_t dwell_time(dwell); - int loop = 0; - boost::uint32_t rb, expected; - - //register singal handler - std::signal(SIGINT, &sig_int_handler); - - //Test the mask - only need to test once with no dwell time - std::cout << "\nTesting mask..." << std::flush; - //send a value of all 1's to the DDR with a mask for only bit 10 - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ~0, FPGPIO_BIT(10)); - //bit 10 should now be 1, but all the other bits should be unchanged - rb = usrp->get_gpio_attr(fpgpio, std::string("DDR")) & mask; - expected = ddr | FPGPIO_BIT(10); - if (rb == expected) - std::cout << "pass" << std::endl; - else - std::cout << "fail" << std::endl; - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - usrp->set_gpio_attr(fpgpio, std::string("DDR"), ddr, mask); - - while (not stop_signal_called) - { - int failures = 0; - - if (vm.count("repeat")) - std::cout << "Press Ctrl + C to quit..." << std::endl; - - // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second - std::cout << "\nTesting user controlled GPIO and ATR idle output..." << std::flush; - usrp->set_gpio_attr(fpgpio, "OUT", 1 << 4, 1 << 4); - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(4) | FPGPIO_BIT(0); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - if ((rb & FPGPIO_BIT(0)) == 0) - std::cout << "Bit 0 should be set, but is not" << std::endl; - if ((rb & FPGPIO_BIT(4)) == 0) - std::cout << "Bit 4 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - usrp->set_gpio_attr(fpgpio, "OUT", 0, FPGPIO_BIT(4)); - if (stop_signal_called) - break; - - // test ATR RX by receiving for 1 second - std::cout << "\nTesting ATR RX output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(1); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 1 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - //clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - if (stop_signal_called) - break; - - // test ATR TX by transmitting for 1 second - std::cout << "\nTesting ATR TX output..." << std::flush; - stop_time = usrp->get_time_now() + dwell_time; - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(2); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 2 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch(...){} - if (stop_signal_called) - break; - - // test ATR RX by transmitting and receiving for 1 second - std::cout << "\nTesting ATR full duplex output..." << std::flush; - rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - rx_stream->issue_stream_cmd(rx_cmd); - tx_md.start_of_burst = true; - tx_md.end_of_burst = false; - stop_time = usrp->get_time_now() + dwell_time; - while (not stop_signal_called and usrp->get_time_now() < stop_time) - { - try { - tx_stream->send(rx_buffs, nsamps_per_buff, tx_md, timeout); - tx_md.start_of_burst = false; - rx_stream->recv(tx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - } - rb = usrp->get_gpio_attr(fpgpio, "READBACK"); - expected = FPGPIO_BIT(3); - if ((rb & expected) != expected) - { - ++failures; - std::cout << "fail" << std::endl; - std::cout << "Bit 3 should be set, but is not" << std::endl; - } else { - std::cout << "pass" << std::endl; - } - std::cout << std::endl; - output_reg_values(fpgpio, usrp, num_bits); - rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - tx_md.end_of_burst = true; - try { - tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); - } catch(...){} - //clear out any data left in the rx stream - try { - rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); - } catch(...){} - - std::cout << std::endl; - if (failures) - std::cout << failures << " tests failed" << std::endl; - else - std::cout << "All tests passed!" << std::endl; - - if (!vm.count("repeat")) - break; - - std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; - } - - //finished - std::cout << std::endl << "Done!" << std::endl << std::endl; - - return EXIT_SUCCESS; -} diff --git a/host/examples/getopt/CMakeLists.txt b/host/examples/getopt/CMakeLists.txt new file mode 100644 index 000000000..46ad1460c --- /dev/null +++ b/host/examples/getopt/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2015 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# getopt library for C examples since MSVC does not include it +######################################################################## +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIRECTORY}) +ADD_LIBRARY(getopt STATIC + ${CMAKE_CURRENT_SOURCE_DIR}/getopt.c +) diff --git a/host/examples/getopt/getopt.c b/host/examples/getopt/getopt.c new file mode 100644 index 000000000..e571ff9c8 --- /dev/null +++ b/host/examples/getopt/getopt.c @@ -0,0 +1,70 @@ +/* + * This file was copied from the following newsgroup posting: + * + * Newsgroups: mod.std.unix + * Subject: public domain AT&T getopt source + * Date: 3 Nov 85 19:34:15 GMT + * + * Here's something you've all been waiting for: the AT&T public domain + * source for getopt(3). It is the code which was given out at the 1985 + * UNIFORUM conference in Dallas. I obtained it by electronic mail + * directly from AT&T. The people there assure me that it is indeed + * in the public domain. + */ + +#include <stdio.h> +#include <string.h> + +#define ERR(szz,czz) if(opterr){fprintf(stderr,"%s%s%c\n",argv[0],szz,czz);} + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(int argc, char **argv, char *opts) +{ + static int sp; + int c; + char *cp; + + sp = 1; + + if (sp == 1) { + if (optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return (EOF); + else if (strcmp(argv[optind], "--") == 0) { + optind++; + return (EOF); + } + } + optopt = c = argv[optind][sp]; + if (c == ':' || (cp = strchr(opts, c)) == NULL) { + ERR(": illegal option -- ", c); + if (argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return ('?'); + } + if (*++cp == ':') { + if (argv[optind][sp + 1] != '\0') + optarg = &argv[optind++][sp + 1]; + else if (++optind >= argc) { + ERR(": option requires an argument -- ", c); + sp = 1; + return ('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if (argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return (c); +} diff --git a/host/examples/getopt/getopt.h b/host/examples/getopt/getopt.h new file mode 100644 index 000000000..ada96ecf0 --- /dev/null +++ b/host/examples/getopt/getopt.h @@ -0,0 +1,19 @@ +/* + * This header is for a function released into the public domain + * by AT&T in 1985. See the newsgroup posting: + * + * Newsgroups: mod.std.unix + * Subject: public domain AT&T getopt source + * Date: 3 Nov 85 19:34:15 GMT + */ +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +extern int optarr; +extern int optind; +extern int optopt; +extern char* optarg; + +int getopt(int argc, char **argv, char *opts); + +#endif /* _GETOPT_H_ */ diff --git a/host/examples/gpio.cpp b/host/examples/gpio.cpp new file mode 100644 index 000000000..b0d15f35a --- /dev/null +++ b/host/examples/gpio.cpp @@ -0,0 +1,462 @@ +// +// Copyright 2014-15 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +// Example for GPIO testing and bit banging. +// +// This example was originally designed to test the 11 bit wide front panel +// GPIO on the X300 series and has since been adapted to work with any GPIO +// bank on any USRP and provide optional bit banging. Please excuse the +// clutter. Also, there is no current way to detect the width of the +// specified GPIO bank, so the user must specify the width with the --bits +// flag if more than 11 bits. +// +// GPIO Testing: +// For testing, GPIO bits are set as follows: +// GPIO[0] = ATR output 1 at idle +// GPIO[1] = ATR output 1 during RX +// GPIO[2] = ATR output 1 during TX +// GPIO[3] = ATR output 1 during full duplex +// GPIO[4] = output +// GPIO[n:5] = input (all other pins) +// The testing cycles through idle, TX, RX, and full duplex, dwelling on each +// test case (default 2 seconds), and then comparing the readback register with +// the expected values of the outputs for verification. The values of all GPIO +// registers are displayed at the end of each test case. Outputs can be +// physically looped back to inputs to manually verify the inputs. +// +// GPIO Bit Banging: +// GPIO banks have the standard registers of DDR for data direction and OUT +// for output values. Users can bit bang the GPIO bits by using this example +// with the --bitbang flag and specifying the --ddr and --out flags to set the +// values of the corresponding registers. The READBACK register is +// continuously read for the duration of the dwell time (default 2 seconds) so +// users can monitor changes on the inputs. +// +// Automatic Transmit/Receive (ATR): +// In addition to the standard DDR and OUT registers, the GPIO banks also +// have ATR (Automatic Transmit/Receive) control registers that allow the +// GPIO pins to be automatically set to specific values when the USRP is +// idle, transmitting, receiving, or operating in full duplex mode. The +// description of these registers is below: +// CTRL - Control (0=manual, 1=ATR) +// ATR_0X - Values to be set when idle +// ATR_RX - Output values to be set when receiving +// ATR_TX - Output values to be set when transmitting +// ATR_XX - Output values to be set when operating in full duplex +// This code below contains examples of setting all these registers. On +// devices with multiple radios, the ATR for the front panel GPIO is driven +// by the state of the first radio (0 or A). +// +// The UHD API +// The multi_usrp::set_gpio_attr() method is the UHD API for configuring and +// controlling the GPIO banks. The parameters to the method are: +// bank - the name of the GPIO bank (typically "FP0" for front panel GPIO, +// "TX<n>" for TX daughter card GPIO, or +// "RX<n>" for RX daughter card GPIO) +// attr - attribute (register) to change ("DDR", "OUT", "CTRL", "ATR_0X", +// "ATR_RX", "ATR_TX", "ATR_XX") +// value - the value to be set +// mask - a mask indicating which bits in the specified attribute register are +// to be changed (default is all bits). + +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/convert.hpp> +#include <boost/assign.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <csignal> +#include <iostream> +#include <stdlib.h> + +static const std::string GPIO_DEFAULT_CPU_FORMAT = "fc32"; +static const std::string GPIO_DEFAULT_OTW_FORMAT = "sc16"; +static const double GPIO_DEFAULT_RX_RATE = 500e3; +static const double GPIO_DEFAULT_TX_RATE = 500e3; +static const double GPIO_DEFAULT_DWELL_TIME = 2.0; +static const std::string GPIO_DEFAULT_GPIO = "FP0"; +static const size_t GPIO_DEFAULT_NUM_BITS = 11; +static const std::string GPIO_DEFAULT_CTRL = "0x0"; // all as user controlled +static const std::string GPIO_DEFAULT_DDR = "0x0"; // all as inputs +static const std::string GPIO_DEFAULT_OUT = "0x0"; + +static UHD_INLINE boost::uint32_t GPIO_BIT(const size_t x) +{ + return (1 << x); +} + +namespace po = boost::program_options; + +static bool stop_signal_called = false; +void sig_int_handler(int){stop_signal_called = true;} + +std::string to_bit_string(boost::uint32_t val, const size_t num_bits) +{ + std::string out; + for (int i = num_bits - 1; i >= 0; i--) + { + std::string bit = ((val >> i) & 1) ? "1" : "0"; + out += " "; + out += bit; + } + return out; +} + +void output_reg_values( + const std::string bank, + const uhd::usrp::multi_usrp::sptr &usrp, + const size_t num_bits) +{ + std::vector<std::string> attrs = boost::assign::list_of("CTRL")("DDR")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX")("OUT")("READBACK"); + std::cout << (boost::format("%10s ") % "Bit"); + for (int i = num_bits - 1; i >= 0; i--) + std::cout << (boost::format(" %2d") % i); + std::cout << std::endl; + BOOST_FOREACH(std::string &attr, attrs) + { + std::cout << (boost::format("%10s:%s") + % attr % to_bit_string(boost::uint32_t(usrp->get_gpio_attr(bank, attr)), num_bits)) + << std::endl; + } +} + +int UHD_SAFE_MAIN(int argc, char *argv[]) +{ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + std::string cpu, otw; + double rx_rate, tx_rate, dwell; + std::string gpio; + size_t num_bits; + std::string ctrl_str; + std::string ddr_str; + std::string out_str; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args") + ("repeat", "repeat loop until Ctrl-C is pressed") + ("cpu", po::value<std::string>(&cpu)->default_value(GPIO_DEFAULT_CPU_FORMAT), "cpu data format") + ("otw", po::value<std::string>(&otw)->default_value(GPIO_DEFAULT_OTW_FORMAT), "over the wire data format") + ("rx_rate", po::value<double>(&rx_rate)->default_value(GPIO_DEFAULT_RX_RATE), "rx sample rate") + ("tx_rate", po::value<double>(&tx_rate)->default_value(GPIO_DEFAULT_TX_RATE), "tx sample rate") + ("dwell", po::value<double>(&dwell)->default_value(GPIO_DEFAULT_DWELL_TIME), "dwell time in seconds for each test case") + ("bank", po::value<std::string>(&gpio)->default_value(GPIO_DEFAULT_GPIO), "name of gpio bank") + ("bits", po::value<size_t>(&num_bits)->default_value(GPIO_DEFAULT_NUM_BITS), "number of bits in gpio bank") + ("bitbang", "single test case where user sets values for CTRL, DDR, and OUT registers") + ("ddr", po::value<std::string>(&ddr_str)->default_value(GPIO_DEFAULT_DDR), "GPIO DDR reg value") + ("out", po::value<std::string>(&out_str)->default_value(GPIO_DEFAULT_OUT), "GPIO OUT reg value") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("gpio %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); + std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; + + //print out initial unconfigured state of FP GPIO + std::cout << "Initial GPIO values:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + + //configure GPIO registers + boost::uint32_t ddr = strtoul(ddr_str.c_str(), NULL, 0); + boost::uint32_t out = strtoul(out_str.c_str(), NULL, 0); + boost::uint32_t ctrl = 0; + boost::uint32_t atr_idle = 0; + boost::uint32_t atr_rx = 0; + boost::uint32_t atr_tx = 0; + boost::uint32_t atr_duplex = 0; + boost::uint32_t mask = (1 << num_bits) - 1; + + if (!vm.count("bitbang")) + { + //set up GPIO outputs: + //GPIO[0] = ATR output 1 at idle + ctrl |= GPIO_BIT(0); + atr_idle |= GPIO_BIT(0); + ddr |= GPIO_BIT(0); + + //GPIO[1] = ATR output 1 during RX + ctrl |= GPIO_BIT(1); + ddr |= GPIO_BIT(1); + atr_rx |= GPIO_BIT(1); + + //GPIO[2] = ATR output 1 during TX + ctrl |= GPIO_BIT(2); + ddr |= GPIO_BIT(2); + atr_tx |= GPIO_BIT(2); + + //GPIO[3] = ATR output 1 during full duplex + ctrl |= GPIO_BIT(3); + ddr |= GPIO_BIT(3); + atr_duplex |= GPIO_BIT(3); + + //GPIO[4] = output + ddr |= GPIO_BIT(4); + } + + //set data direction register (DDR) + usrp->set_gpio_attr(gpio, "DDR", ddr, mask); + + //set output values (OUT) + usrp->set_gpio_attr(gpio, "OUT", out, mask); + + //set ATR registers + usrp->set_gpio_attr(gpio, "ATR_0X", atr_idle, mask); + usrp->set_gpio_attr(gpio, "ATR_RX", atr_rx, mask); + usrp->set_gpio_attr(gpio, "ATR_TX", atr_tx, mask); + usrp->set_gpio_attr(gpio, "ATR_XX", atr_duplex, mask); + + //set control register + usrp->set_gpio_attr(gpio, "CTRL", ctrl, mask); + + //print out initial state of FP GPIO + std::cout << "\nConfigured GPIO values:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + std::cout << std::endl; + + //set up streams + uhd::stream_args_t rx_args(cpu, otw); + uhd::stream_args_t tx_args(cpu, otw); + uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(rx_args); + uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(tx_args); + uhd::stream_cmd_t rx_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + rx_cmd.stream_now = true; + usrp->set_rx_rate(rx_rate); + usrp->set_tx_rate(tx_rate); + + //set up buffers for tx and rx + const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); + const size_t nsamps_per_buff = max_samps_per_packet; + std::vector<char> rx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<char> tx_buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(cpu)); + std::vector<void *> rx_buffs, tx_buffs; + for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) + rx_buffs.push_back(&rx_buff.front()); //same buffer for each channel + for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) + tx_buffs.push_back(&tx_buff.front()); //same buffer for each channel + + uhd::rx_metadata_t rx_md; + uhd::tx_metadata_t tx_md; + tx_md.has_time_spec = false; + tx_md.start_of_burst = true; + uhd::time_spec_t stop_time; + double timeout = 0.01; + uhd::time_spec_t dwell_time(dwell); + int loop = 0; + boost::uint32_t rb, expected; + + //register signal handler + std::signal(SIGINT, &sig_int_handler); + + if (!vm.count("bitbang")) + { + // Test the mask parameter of the multi_usrp::set_gpio_attr API + // We only need to test once with no dwell time + std::cout << "\nTesting mask..." << std::flush; + //send a value of all 1's to the DDR with a mask for only upper most bit + usrp->set_gpio_attr(gpio, "DDR", ~0, GPIO_BIT(num_bits - 1)); + //upper most bit should now be 1, but all the other bits should be unchanged + rb = usrp->get_gpio_attr(gpio, "DDR") & mask; + expected = ddr | GPIO_BIT(num_bits - 1); + if (rb == expected) + std::cout << "pass:" << std::endl; + else + std::cout << "fail:" << std::endl; + output_reg_values(gpio, usrp, num_bits); + //restore DDR value + usrp->set_gpio_attr(gpio, "DDR", ddr, mask); + } + + while (not stop_signal_called) + { + int failures = 0; + + if (vm.count("repeat")) + std::cout << "Press Ctrl + C to quit..." << std::endl; + + if (vm.count("bitbang")) + { + // dwell and continuously read back GPIO values + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + rb = usrp->get_gpio_attr(gpio, "READBACK"); + std::cout << "\rREADBACK: " << to_bit_string(rb, num_bits); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + std::cout << std::endl; + } + else + { + // test user controlled GPIO and ATR idle by setting bit 4 high for 1 second + std::cout << "\nTesting user controlled GPIO and ATR idle output..." << std::flush; + usrp->set_gpio_attr(gpio, "OUT", 1 << 4, 1 << 4); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(4) | GPIO_BIT(0); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + if ((rb & GPIO_BIT(0)) == 0) + std::cout << "Bit 0 should be set, but is not" << std::endl; + if ((rb & GPIO_BIT(4)) == 0) + std::cout << "Bit 4 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + usrp->set_gpio_attr(gpio, "OUT", 0, GPIO_BIT(4)); + if (stop_signal_called) + break; + + // test ATR RX by receiving for 1 second + std::cout << "\nTesting ATR RX output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(1); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 1 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR TX by transmitting for 1 second + std::cout << "\nTesting ATR TX output..." << std::flush; + stop_time = usrp->get_time_now() + dwell_time; + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(2); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 2 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + if (stop_signal_called) + break; + + // test ATR RX by transmitting and receiving for 1 second + std::cout << "\nTesting ATR full duplex output..." << std::flush; + rx_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + rx_stream->issue_stream_cmd(rx_cmd); + tx_md.start_of_burst = true; + tx_md.end_of_burst = false; + stop_time = usrp->get_time_now() + dwell_time; + while (not stop_signal_called and usrp->get_time_now() < stop_time) + { + try { + tx_stream->send(rx_buffs, nsamps_per_buff, tx_md, timeout); + tx_md.start_of_burst = false; + rx_stream->recv(tx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + } + rb = usrp->get_gpio_attr(gpio, "READBACK"); + expected = GPIO_BIT(3); + if ((rb & expected) != expected) + { + ++failures; + std::cout << "fail:" << std::endl; + std::cout << "Bit 3 should be set, but is not" << std::endl; + } else { + std::cout << "pass:" << std::endl; + } + output_reg_values(gpio, usrp, num_bits); + rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + tx_md.end_of_burst = true; + try { + tx_stream->send(tx_buffs, nsamps_per_buff, tx_md, timeout); + } catch(...){} + //clear out any data left in the rx stream + try { + rx_stream->recv(rx_buffs, nsamps_per_buff, rx_md, timeout); + } catch(...){} + + std::cout << std::endl; + if (failures) + std::cout << failures << " tests failed" << std::endl; + else + std::cout << "All tests passed!" << std::endl; + } + + if (!vm.count("repeat")) + break; + + if (not stop_signal_called) + std::cout << (boost::format("\nLoop %d completed") % ++loop) << std::endl; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return EXIT_SUCCESS; +} diff --git a/host/examples/rx_samples_c.c b/host/examples/rx_samples_c.c new file mode 100644 index 000000000..abea812fe --- /dev/null +++ b/host/examples/rx_samples_c.c @@ -0,0 +1,291 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <uhd.h> + +#include "getopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +void print_help(void){ + fprintf(stderr, "rx_samples_c - A simple RX example using UHD's C API\n\n" + + "Options:\n" + " -a (device args)\n" + " -f (frequency in Hz)\n" + " -r (sample rate in Hz)\n" + " -g (gain)\n" + " -n (number of samples to receive)\n" + " -o (output filename, default = \"out.dat\")\n" + " -v (enable verbose prints)\n" + " -h (print this help message)\n"); +}; + +int main(int argc, char* argv[]) +{ + if(uhd_set_thread_priority(uhd_default_thread_priority, true)){ + fprintf(stderr, "Unable to set thread priority. Continuing anyway.\n"); + } + + int option = 0; + double freq = 500e6; + double rate = 1e6; + double gain = 5.0; + char* device_args = ""; + size_t channel = 0; + char* filename = "out.dat"; + size_t n_samples = 1000000; + bool verbose = false; + int return_code = EXIT_SUCCESS; + bool custom_filename = false; + char error_string[512]; + + // Process options + while((option = getopt(argc, argv, "a:f:r:g:n:o:vh")) != -1){ + switch(option){ + case 'a': + device_args = strdup(optarg); + break; + + case 'f': + freq = atof(optarg); + break; + + case 'r': + rate = atof(optarg); + break; + + case 'g': + gain = atof(optarg); + break; + + case 'n': + n_samples = atoi(optarg); + break; + + case 'o': + filename = strdup(optarg); + custom_filename = true; + break; + + case 'v': + verbose = true; + break; + + case 'h': + print_help(); + goto free_option_strings; + + default: + print_help(); + return_code = EXIT_FAILURE; + goto free_option_strings; + } + } + + // Create USRP + uhd_usrp_handle usrp; + fprintf(stderr, "Creating USRP with args \"%s\"...\n", device_args); + EXECUTE_OR_GOTO(free_option_strings, + uhd_usrp_make(&usrp, device_args) + ) + + // Create RX streamer + uhd_rx_streamer_handle rx_streamer; + EXECUTE_OR_GOTO(free_usrp, + uhd_rx_streamer_make(&rx_streamer) + ) + + // Create RX metadata + uhd_rx_metadata_handle md; + EXECUTE_OR_GOTO(free_rx_streamer, + uhd_rx_metadata_make(&md) + ) + + // Create other necessary structs + uhd_tune_request_t tune_request = { + .target_freq = freq, + .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + }; + uhd_tune_result_t tune_result; + + uhd_stream_args_t stream_args = { + .cpu_format = "fc32", + .otw_format = "sc16", + .args = "", + .channel_list = &channel, + .n_channels = 1 + }; + + uhd_stream_cmd_t stream_cmd = { + .stream_mode = UHD_STREAM_MODE_NUM_SAMPS_AND_DONE, + .num_samps = n_samples, + .stream_now = true + }; + + size_t samps_per_buff; + float *buff = NULL; + void **buffs_ptr = NULL; + FILE *fp = NULL; + size_t num_acc_samps = 0; + + // Set rate + fprintf(stderr, "Setting RX Rate: %f...\n", rate); + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_set_rx_rate(usrp, rate, channel) + ) + + // See what rate actually is + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_get_rx_rate(usrp, channel, &rate) + ) + fprintf(stderr, "Actual RX Rate: %f...\n", rate); + + // Set gain + fprintf(stderr, "Setting RX Gain: %f dB...\n", gain); + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_set_rx_gain(usrp, gain, channel, "") + ) + + // See what gain actually is + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_get_rx_gain(usrp, channel, "", &gain) + ) + fprintf(stderr, "Actual RX Gain: %f...\n", gain); + + // Set frequency + fprintf(stderr, "Setting RX frequency: %f MHz...\n", freq/1e6); + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_set_rx_freq(usrp, &tune_request, channel, &tune_result) + ) + + // See what frequency actually is + EXECUTE_OR_GOTO(free_rx_metadata, + uhd_usrp_get_rx_freq(usrp, channel, &freq) + ) + fprintf(stderr, "Actual RX frequency: %f MHz...\n", freq / 1e6); + + // Set up streamer + stream_args.channel_list = &channel; + EXECUTE_OR_GOTO(free_rx_streamer, + uhd_usrp_get_rx_stream(usrp, &stream_args, rx_streamer) + ) + + // Set up buffer + EXECUTE_OR_GOTO(free_rx_streamer, + uhd_rx_streamer_max_num_samps(rx_streamer, &samps_per_buff) + ) + fprintf(stderr, "Buffer size in samples: %zu\n", samps_per_buff); + buff = malloc(samps_per_buff * 2 * sizeof(float)); + buffs_ptr = (void**)&buff; + + // Issue stream command + fprintf(stderr, "Issuing stream command.\n"); + EXECUTE_OR_GOTO(free_buffer, + uhd_rx_streamer_issue_stream_cmd(rx_streamer, &stream_cmd) + ) + + // Set up file output + fp = fopen(filename, "wb"); + + // Actual streaming + while (num_acc_samps < n_samples) { + size_t num_rx_samps = 0; + EXECUTE_OR_GOTO(close_file, + uhd_rx_streamer_recv(rx_streamer, buffs_ptr, samps_per_buff, &md, 3.0, false, &num_rx_samps) + ) + + uhd_rx_metadata_error_code_t error_code; + EXECUTE_OR_GOTO(close_file, + uhd_rx_metadata_error_code(md, &error_code) + ) + if(return_code != UHD_RX_METADATA_ERROR_CODE_NONE){ + fprintf(stderr, "Error code 0x%x was returned during streaming. Aborting.\n", return_code); + goto close_file; + } + + // Handle data + fwrite(buff, sizeof(float) * 2, num_rx_samps, fp); + if (verbose) { + time_t full_secs; + double frac_secs; + uhd_rx_metadata_time_spec(md, &full_secs, &frac_secs); + fprintf(stderr, "Received packet: %zu samples, %zu full secs, %f frac secs\n", + num_rx_samps, + full_secs, + frac_secs); + } + + num_acc_samps += num_rx_samps; + } + + // Cleanup + close_file: + fclose(fp); + + free_buffer: + if(buff){ + if(verbose){ + fprintf(stderr, "Freeing buffer.\n"); + } + free(buff); + } + buff = NULL; + buffs_ptr = NULL; + + free_rx_streamer: + if(verbose){ + fprintf(stderr, "Cleaning up RX streamer.\n"); + } + uhd_rx_streamer_free(&rx_streamer); + + free_rx_metadata: + if(verbose){ + fprintf(stderr, "Cleaning up RX metadata.\n"); + } + uhd_rx_metadata_free(&md); + + free_usrp: + if(verbose){ + fprintf(stderr, "Cleaning up USRP.\n"); + } + if(return_code != EXIT_SUCCESS && usrp != NULL){ + uhd_usrp_last_error(usrp, error_string, 512); + fprintf(stderr, "USRP reported the following error: %s\n", error_string); + } + uhd_usrp_free(&usrp); + + free_option_strings: + if(strcmp(device_args,"")){ + free(device_args); + } + if(custom_filename){ + free(filename); + } + + fprintf(stderr, (return_code ? "Failure\n" : "Success\n")); + return return_code; +} diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index 80b72de9c..934dce586 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -56,7 +56,7 @@ template<typename samp_type> void recv_to_file( std::vector<samp_type> buff(samps_per_buff); std::ofstream outfile; if (not null) - outfile.open(file.c_str(), std::ofstream::binary); + outfile.open(file.c_str(), std::ofstream::binary); bool overflow_message = true; //setup streaming @@ -78,8 +78,8 @@ template<typename samp_type> void recv_to_file( typedef std::map<size_t,size_t> SizeMap; SizeMap mapSizes; - while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){ - boost::system_time now = boost::get_system_time(); + while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)) { + boost::system_time now = boost::get_system_time(); size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0, enable_size_map); @@ -88,7 +88,7 @@ template<typename samp_type> void recv_to_file( break; } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){ - if (overflow_message){ + if (overflow_message) { overflow_message = false; std::cerr << boost::format( "Got an overflow indication. Please consider the following:\n" @@ -110,99 +110,99 @@ template<typename samp_type> void recv_to_file( throw std::runtime_error(error); } - if (enable_size_map){ - SizeMap::iterator it = mapSizes.find(num_rx_samps); - if (it == mapSizes.end()) - mapSizes[num_rx_samps] = 0; - mapSizes[num_rx_samps] += 1; - } + if (enable_size_map) { + SizeMap::iterator it = mapSizes.find(num_rx_samps); + if (it == mapSizes.end()) + mapSizes[num_rx_samps] = 0; + mapSizes[num_rx_samps] += 1; + } num_total_samps += num_rx_samps; - if (outfile.is_open()) - outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); - - if (bw_summary){ - last_update_samps += num_rx_samps; - boost::posix_time::time_duration update_diff = now - last_update; - if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { - double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); - double r = (double)last_update_samps / t; - std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; - last_update_samps = 0; - last_update = now; - } - } + if (outfile.is_open()) + outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type)); + + if (bw_summary) { + last_update_samps += num_rx_samps; + boost::posix_time::time_duration update_diff = now - last_update; + if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) { + double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + double r = (double)last_update_samps / t; + std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl; + last_update_samps = 0; + last_update = now; + } + } ticks_diff = now - start; - if (ticks_requested > 0){ - if ((unsigned long long)ticks_diff.ticks() > ticks_requested) - break; - } + if (ticks_requested > 0){ + if ((unsigned long long)ticks_diff.ticks() > ticks_requested) + break; + } } stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; rx_stream->issue_stream_cmd(stream_cmd); if (outfile.is_open()) - outfile.close(); - - if (stats){ - std::cout << std::endl; - - double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); - std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; - double r = (double)num_total_samps / t; - std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; - - if (enable_size_map) { - std::cout << std::endl; - std::cout << "Packet size map (bytes: count)" << std::endl; - for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) - std::cout << it->first << ":\t" << it->second << std::endl; - } - } + outfile.close(); + + if (stats) { + std::cout << std::endl; + + double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second(); + std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl; + double r = (double)num_total_samps / t; + std::cout << boost::format("%f Msps") % (r/1e6) << std::endl; + + if (enable_size_map) { + std::cout << std::endl; + std::cout << "Packet size map (bytes: count)" << std::endl; + for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++) + std::cout << it->first << ":\t" << it->second << std::endl; + } + } } typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t; bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){ - if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) - return false; - - boost::system_time start = boost::get_system_time(); - boost::system_time first_lock_time; - - std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; - std::cout.flush(); - - while (true){ - if ((not first_lock_time.is_not_a_date_time()) and - (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) - { - std::cout << " locked." << std::endl; - break; - } - if (get_sensor_fn(sensor_name).to_bool()){ - if (first_lock_time.is_not_a_date_time()) - first_lock_time = boost::get_system_time(); - std::cout << "+"; - std::cout.flush(); - } - else{ - first_lock_time = boost::system_time(); //reset to 'not a date time' - - if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ - std::cout << std::endl; - throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); - } - std::cout << "_"; - std::cout.flush(); - } - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - std::cout << std::endl; - return true; + if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) + return false; + + boost::system_time start = boost::get_system_time(); + boost::system_time first_lock_time; + + std::cout << boost::format("Waiting for \"%s\": ") % sensor_name; + std::cout.flush(); + + while (true) { + if ((not first_lock_time.is_not_a_date_time()) and + (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time)))) + { + std::cout << " locked." << std::endl; + break; + } + if (get_sensor_fn(sensor_name).to_bool()){ + if (first_lock_time.is_not_a_date_time()) + first_lock_time = boost::get_system_time(); + std::cout << "+"; + std::cout.flush(); + } + else { + first_lock_time = boost::system_time(); //reset to 'not a date time' + + if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){ + std::cout << std::endl; + throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name)); + } + std::cout << "_"; + std::cout.flush(); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + std::cout << std::endl; + return true; } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -246,8 +246,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ po::notify(vm); //print the help message - if (vm.count("help")){ + if (vm.count("help")) { std::cout << boost::format("UHD RX samples to file %s") % desc << std::endl; + std::cout + << std::endl + << "This application streams data from a single channel of a USRP device to a file.\n" + << std::endl; return ~0; } @@ -258,7 +262,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ bool continue_on_bad_packet = vm.count("continue") > 0; if (enable_size_map) - std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; + std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl; //create a usrp device std::cout << std::endl; @@ -283,23 +287,23 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl; //set the center frequency - if (vm.count("freq")){ //with default of 0.0 this will always be true - std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; + if (vm.count("freq")) { //with default of 0.0 this will always be true + std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl; uhd::tune_request_t tune_request(freq); if(vm.count("int-n")) tune_request.args = uhd::device_addr_t("mode_n=integer"); - usrp->set_rx_freq(tune_request); - std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; - } + usrp->set_rx_freq(tune_request); + std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl; + } //set the rf gain - if (vm.count("gain")){ + if (vm.count("gain")) { std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl; usrp->set_rx_gain(gain); std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl; } - //set the analog frontend filter bandwidth - if (vm.count("bw")){ + //set the IF filter bandwidth + if (vm.count("bw")) { std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw/1e6) << std::endl; usrp->set_rx_bandwidth(bw); std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp->get_rx_bandwidth()/1e6) << std::endl << std::endl; @@ -312,12 +316,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //check Ref and LO Lock detect if (not vm.count("skip-lo")){ - check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); - if (ref == "mimo") - check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); - if (ref == "external") - check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); - } + check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time); + if (ref == "mimo") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + if (ref == "external") + check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time); + } if (total_num_samps == 0){ std::signal(SIGINT, &sig_int_handler); @@ -325,7 +329,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } #define recv_to_file_args(format) \ - (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) + (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet) //recv to file if (type == "double") recv_to_file<std::complex<double> >recv_to_file_args("fc64"); else if (type == "float") recv_to_file<std::complex<float> >recv_to_file_args("fc32"); diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 30535907f..20abd92fe 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -48,7 +48,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of incoming samples") ("dilv", "specify to disable inner-loop verbose") ("channels", po::value<std::string>(&channel_list)->default_value("0"), "which channel(s) to use (specify \"0\", \"1\", \"0,1\", etc)") - ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); diff --git a/host/examples/test_clock_synch.cpp b/host/examples/test_clock_synch.cpp index 9d1883665..2d438c5ca 100644 --- a/host/examples/test_clock_synch.cpp +++ b/host/examples/test_clock_synch.cpp @@ -124,7 +124,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //Get GPS time to initially set USRP devices std::cout << std::endl << "Querying Clock for time and setting USRP times..." << std::endl << std::endl; - boost::uint32_t clock_time = clock->get_time(); + time_t clock_time = clock->get_time(); usrp->set_time_unknown_pps(uhd::time_spec_t(double(clock_time+2))); //Wait for next PPS to start polling diff --git a/host/examples/test_pps_input.cpp b/host/examples/test_pps_input.cpp index 889c98a45..3e6c4ba9d 100644 --- a/host/examples/test_pps_input.cpp +++ b/host/examples/test_pps_input.cpp @@ -47,6 +47,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ std::cout << boost::format("UHD Test PPS Input %s") % desc << std::endl; + std::cout + << std::endl + << "Tests if the PPS input signal is working. Will throw an error if not." + << std::endl + << std::endl; return ~0; } diff --git a/host/examples/transport_hammer.cpp b/host/examples/transport_hammer.cpp deleted file mode 100644 index 32e344e3e..000000000 --- a/host/examples/transport_hammer.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// -// Copyright 2012 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/utils/thread_priority.hpp> -#include <uhd/convert.hpp> -#include <uhd/utils/safe_main.hpp> -#include <uhd/usrp/multi_usrp.hpp> -#include <boost/program_options.hpp> -#include <boost/format.hpp> -#include <boost/thread/thread.hpp> -#include <boost/math/special_functions/round.hpp> -#include <iostream> -#include <complex> - -namespace po = boost::program_options; - -/*********************************************************************** - * Test result variables - **********************************************************************/ -unsigned long long num_overflows = 0; -unsigned long long num_underflows = 0; -unsigned long long num_rx_samps = 0; -unsigned long long num_tx_samps = 0; -unsigned long long num_dropped_samps = 0; -unsigned long long num_seq_errors = 0; - -/*********************************************************************** - * RX Hammer - **********************************************************************/ -void rx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &rx_cpu, uhd::rx_streamer::sptr rx_stream){ - uhd::set_thread_priority_safe(); - - //print pre-test summary - std::cout << boost::format( - "Testing receive rate %f Msps" - ) % (usrp->get_rx_rate()/1e6) << std::endl; - - //setup variables and allocate buffer - uhd::rx_metadata_t md; - const size_t max_samps_per_packet = rx_stream->get_max_num_samps(); - std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(rx_cpu)); - std::vector<void *> buffs; - for (size_t ch = 0; ch < rx_stream->get_num_channels(); ch++) - buffs.push_back(&buff.front()); //same buffer for each channel - bool had_an_overflow = false; - uhd::time_spec_t last_time; - const double rate = usrp->get_rx_rate(); - double timeout = 1; - - uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); - cmd.time_spec = usrp->get_time_now() + uhd::time_spec_t(0.05); - cmd.stream_now = (buffs.size() == 1); - srand( time(NULL) ); - - while (not boost::this_thread::interruption_requested()){ - cmd.num_samps = rand() % 100000; - rx_stream->issue_stream_cmd(cmd); - num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md, timeout, true); - - //handle the error codes - switch(md.error_code){ - case uhd::rx_metadata_t::ERROR_CODE_NONE: - if (had_an_overflow){ - had_an_overflow = false; - num_dropped_samps += boost::math::iround((md.time_spec - last_time).get_real_secs()*rate); - } - break; - - case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: - had_an_overflow = true; - last_time = md.time_spec; - if (!md.out_of_sequence) - num_overflows++; - break; - - default: - std::cerr << "Receiver error: " << md.strerror() << std::endl; - std::cerr << "Unexpected error on recv, continuing..." << std::endl; - break; - } - } -} - -/*********************************************************************** - * TX Hammer - **********************************************************************/ -void tx_hammer(uhd::usrp::multi_usrp::sptr usrp, const std::string &tx_cpu, uhd::tx_streamer::sptr tx_stream){ - uhd::set_thread_priority_safe(); - - uhd::tx_metadata_t md; - const size_t max_samps_per_packet = tx_stream->get_max_num_samps(); - std::vector<char> buff(max_samps_per_packet*uhd::convert::get_bytes_per_item(tx_cpu)); - std::vector<void *> buffs; - for (size_t ch = 0; ch < tx_stream->get_num_channels(); ch++) - buffs.push_back(&buff.front()); //same buffer for each channel - - //print pre-test summary - std::cout << boost::format( - "Testing transmit rate %f Msps" - ) % (usrp->get_tx_rate()/1e6) << std::endl; - - //setup variables and allocate buffer - std::srand( time(NULL) ); - while(not boost::this_thread::interruption_requested()){ - size_t total_num_samps = rand() % 100000; - size_t num_acc_samps = 0; - float timeout = 1; - - usrp->set_time_now(uhd::time_spec_t(0.0)); - while(num_acc_samps < total_num_samps){ - - //send a single packet - num_tx_samps += tx_stream->send(buffs, max_samps_per_packet, md, timeout); - - num_acc_samps += std::min(total_num_samps-num_acc_samps, tx_stream->get_max_num_samps()); - } - //send a mini EOB packet - md.end_of_burst = true; - tx_stream->send("", 0, md); - } -} - -void tx_hammer_async_helper(uhd::tx_streamer::sptr tx_stream){ - //setup variables and allocate buffer - uhd::async_metadata_t async_md; - - while (not boost::this_thread::interruption_requested()){ - - if (not tx_stream->recv_async_msg(async_md)) continue; - - //handle the error codes - switch(async_md.event_code){ - case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: - return; - - case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: - case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET: - num_underflows++; - break; - - case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR: - case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST: - num_seq_errors++; - break; - - default: - std::cerr << "Event code: " << async_md.event_code << std::endl; - std::cerr << "Unexpected event on async recv, continuing..." << std::endl; - break; - } - } -} - -/*********************************************************************** - * Main code + dispatcher - **********************************************************************/ -int UHD_SAFE_MAIN(int argc, char *argv[]){ - uhd::set_thread_priority_safe(); - - //variables to be set by po - std::string args; - double duration; - double rx_rate, tx_rate; - std::string rx_otw, tx_otw; - std::string rx_cpu, tx_cpu; - std::string mode; - - //setup the program options - po::options_description desc("Allowed options"); - desc.add_options() - ("help", "help message") - ("args", po::value<std::string>(&args)->default_value(""), "single uhd device address args") - ("duration", po::value<double>(&duration)->default_value(10.0), "if random, specify duration for the test in seconds") - ("rx_rate", po::value<double>(&rx_rate), "specify to perform a RX rate test (sps)") - ("tx_rate", po::value<double>(&tx_rate), "specify to perform a TX rate test (sps)") - ("rx_otw", po::value<std::string>(&rx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for RX") - ("tx_otw", po::value<std::string>(&tx_otw)->default_value("sc16"), "specify the over-the-wire sample mode for TX") - ("rx_cpu", po::value<std::string>(&rx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for RX") - ("tx_cpu", po::value<std::string>(&tx_cpu)->default_value("fc32"), "specify the host/cpu sample mode for TX") - ("mode", po::value<std::string>(&mode)->default_value("none"), "multi-channel sync mode option: none, mimo") - ; - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - //print the help message - if (vm.count("help") or (vm.count("rx_rate") + vm.count("tx_rate")) == 0){ - //std::cout << boost::format("UHD Transport Hammer - %s") % desc << std::endl; - std::cout << - "UHD Transport Hammer: a transport layer stress test that continuously\n" - "calls for random amounts of TX and RX samples\n\n"; - std::cout << desc << std::endl << - " Specify --rx_rate for a receive-only test.\n" - " Specify --tx_rate for a transmit-only test.\n" - " Specify both options for a full-duplex test.\n" - << std::endl; - return ~0; - } - - //create a usrp device - std::cout << std::endl; - uhd::device_addrs_t device_addrs = uhd::device::find(args, uhd::device::USRP); - if (not device_addrs.empty() and device_addrs.at(0).get("type", "") == "usrp1"){ - std::cerr << "*** Warning! ***" << std::endl; - std::cerr << "Results will be inaccurate on USRP1 due to insufficient features.\n" << std::endl; - } - std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; - uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args); - std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl; - - if (mode == "mimo"){ - usrp->set_clock_source("mimo", 0); - usrp->set_time_source("mimo", 0); - boost::this_thread::sleep(boost::posix_time::seconds(1)); - } - - boost::thread_group thread_group; - - //spawn the receive test thread - if (vm.count("rx_rate")){ - usrp->set_rx_rate(rx_rate); - //create a receive streamer - uhd::stream_args_t stream_args(rx_cpu, rx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); - thread_group.create_thread(boost::bind(&rx_hammer, usrp, rx_cpu, rx_stream)); - } - - //spawn the transmit test thread - if (vm.count("tx_rate")){ - usrp->set_tx_rate(tx_rate); - //create a transmit streamer - uhd::stream_args_t stream_args(tx_cpu, tx_otw); - for (size_t ch = 0; ch < usrp->get_num_mboards(); ch++) //linear channel mapping - stream_args.channels.push_back(ch); - uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args); - thread_group.create_thread(boost::bind(&tx_hammer, usrp, tx_cpu, tx_stream)); - thread_group.create_thread(boost::bind(&tx_hammer_async_helper, tx_stream)); - } - - //sleep for the required duration - const long secs = long(duration); - const long usecs = long((duration - secs)*1e6); - boost::this_thread::sleep(boost::posix_time::seconds(secs) + boost::posix_time::microseconds(usecs)); - - //interrupt and join the threads - thread_group.interrupt_all(); - thread_group.join_all(); - - //print summary - std::cout << std::endl << boost::format( - "Transport Hammer summary:\n" - " Num received samples: %u\n" - " Num dropped samples: %u\n" - " Num overflows detected: %u\n" - " Num transmitted samples: %u\n" - " Num sequence errors: %u\n" - " Num underflows detected: %u\n" - ) % num_rx_samps % num_dropped_samps % num_overflows % num_tx_samps % num_seq_errors % num_underflows << std::endl; - - //finished - std::cout << std::endl << "Done!" << std::endl << std::endl; - - return EXIT_SUCCESS; -} diff --git a/host/examples/tx_bursts.cpp b/host/examples/tx_bursts.cpp index fec89a0e4..bb71d4581 100644 --- a/host/examples/tx_bursts.cpp +++ b/host/examples/tx_bursts.cpp @@ -148,7 +148,6 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t num_tx_samps = tx_stream->send( buffs, samps_to_send, md, timeout ); - //do not use time spec for subsequent packets md.has_time_spec = false; md.start_of_burst = false; diff --git a/host/examples/tx_samples_c.c b/host/examples/tx_samples_c.c new file mode 100644 index 000000000..ebf368ec7 --- /dev/null +++ b/host/examples/tx_samples_c.c @@ -0,0 +1,241 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <uhd.h> + +#include "getopt.h" + +#include <math.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +void print_help(void){ + fprintf(stderr, "tx_samples_c - A simple TX example using UHD's C API\n\n" + + "Options:\n" + " -a (device args)\n" + " -f (frequency in Hz)\n" + " -r (sample rate in Hz)\n" + " -g (gain)\n" + " -v (enable verbose prints)\n" + " -h (print this help message)\n"); +} + +bool stop_signal_called = false; + +void sigint_handler(int code){ + (void)code; + stop_signal_called = true; +} + +int main(int argc, char* argv[]){ + int option = 0; + double freq = 2e9; + double rate = 1e6; + double gain = 0; + char* device_args = ""; + size_t channel = 0; + bool verbose = false; + int return_code = EXIT_SUCCESS; + char error_string[512]; + + // Process options + while((option = getopt(argc, argv, "a:f:r:g:vh")) != -1){ + switch(option){ + case 'a': + device_args = strdup(optarg); + break; + + case 'f': + freq = atof(optarg); + break; + + case 'r': + rate = atof(optarg); + break; + + case 'g': + gain = atof(optarg); + break; + + case 'v': + verbose = true; + break; + + case 'h': + print_help(); + goto free_option_strings; + + default: + print_help(); + return_code = EXIT_FAILURE; + goto free_option_strings; + } + } + + if(uhd_set_thread_priority(uhd_default_thread_priority, true)){ + fprintf(stderr, "Unable to set thread priority. Continuing anyway.\n"); + } + + // Create USRP + uhd_usrp_handle usrp; + fprintf(stderr, "Creating USRP with args \"%s\"...\n", device_args); + EXECUTE_OR_GOTO(free_option_strings, + uhd_usrp_make(&usrp, device_args) + ) + + // Create TX streamer + uhd_tx_streamer_handle tx_streamer; + EXECUTE_OR_GOTO(free_usrp, + uhd_tx_streamer_make(&tx_streamer) + ) + + // Create TX metadata + uhd_tx_metadata_handle md; + EXECUTE_OR_GOTO(free_tx_streamer, + uhd_tx_metadata_make(&md, false, 0.0, 0.1, true, false) + ) + + // Create other necessary structs + uhd_tune_request_t tune_request = { + .target_freq = freq, + .rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO, + .dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO + }; + uhd_tune_result_t tune_result; + + uhd_stream_args_t stream_args = { + .cpu_format = "fc32", + .otw_format = "sc16", + .args = "", + .channel_list = &channel, + .n_channels = 1 + }; + + size_t samps_per_buff; + float* buff = NULL; + const void** buffs_ptr = NULL; + + // Set rate + fprintf(stderr, "Setting TX Rate: %f...\n", rate); + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_set_tx_rate(usrp, rate, channel) + ) + + // See what rate actually is + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_get_tx_rate(usrp, channel, &rate) + ) + fprintf(stderr, "Actual TX Rate: %f...\n\n", rate); + + // Set gain + fprintf(stderr, "Setting TX Gain: %f db...\n", gain); + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_set_tx_gain(usrp, gain, 0, "") + ) + + // See what gain actually is + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_get_tx_gain(usrp, channel, "", &gain) + ) + fprintf(stderr, "Actual TX Gain: %f...\n", gain); + + // Set frequency + fprintf(stderr, "Setting TX frequency: %f MHz...\n", freq / 1e6); + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_set_tx_freq(usrp, &tune_request, channel, &tune_result) + ) + + // See what frequency actually is + EXECUTE_OR_GOTO(free_tx_metadata, + uhd_usrp_get_tx_freq(usrp, channel, &freq) + ) + fprintf(stderr, "Actual TX frequency: %f MHz...\n", freq / 1e6); + + // Set up streamer + stream_args.channel_list = &channel; + EXECUTE_OR_GOTO(free_tx_streamer, + uhd_usrp_get_tx_stream(usrp, &stream_args, tx_streamer) + ) + + // Set up buffer + EXECUTE_OR_GOTO(free_tx_streamer, + uhd_tx_streamer_max_num_samps(tx_streamer, &samps_per_buff) + ) + fprintf(stderr, "Buffer size in samples: %zu\n", samps_per_buff); + buff = malloc(samps_per_buff * 2 * sizeof(float)); + buffs_ptr = (const void**)&buff; + size_t i = 0; + for(i = 0; i < (samps_per_buff*2); i+=2){ + buff[i] = 0.1; + buff[i+1] = 0; + } + + // Ctrl+C will exit loop + signal(SIGINT, &sigint_handler); + fprintf(stderr, "Press Ctrl+C to stop streaming...\n"); + + // Actual streaming + size_t num_samps_sent = 0; + while(!stop_signal_called){ + EXECUTE_OR_GOTO(free_tx_streamer, + uhd_tx_streamer_send(tx_streamer, buffs_ptr, samps_per_buff, &md, 0.1, &num_samps_sent) + ) + if(verbose){ + fprintf(stderr, "Sent %zu samples\n", num_samps_sent); + } + } + + free_tx_streamer: + if(verbose){ + fprintf(stderr, "Cleaning up TX streamer.\n"); + } + uhd_tx_streamer_free(&tx_streamer); + + free_tx_metadata: + if(verbose){ + fprintf(stderr, "Cleaning up TX metadata.\n"); + } + uhd_tx_metadata_free(&md); + + free_usrp: + if(verbose){ + fprintf(stderr, "Cleaning up USRP.\n"); + } + if(return_code != EXIT_SUCCESS && usrp != NULL){ + uhd_usrp_last_error(usrp, error_string, 512); + fprintf(stderr, "USRP reported the following error: %s\n", error_string); + } + uhd_usrp_free(&usrp); + + free_option_strings: + if(strcmp(device_args,"")){ + free(device_args); + } + + fprintf(stderr, (return_code ? "Failure\n" : "Success\n")); + + return return_code; +} diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index d648d2309..942b5df7b 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "wavetable.hpp" #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/static.hpp> @@ -28,9 +29,7 @@ #include <boost/lexical_cast.hpp> #include <boost/algorithm/string.hpp> #include <iostream> -#include <complex> #include <csignal> -#include <cmath> namespace po = boost::program_options; @@ -41,52 +40,6 @@ static bool stop_signal_called = false; void sig_int_handler(int){stop_signal_called = true;} /*********************************************************************** - * Waveform generators - **********************************************************************/ -static const size_t wave_table_len = 8192; - -class wave_table_class{ -public: - wave_table_class(const std::string &wave_type, const float ampl): - _wave_table(wave_table_len) - { - //compute real wave table with 1.0 amplitude - std::vector<double> real_wave_table(wave_table_len); - if (wave_type == "CONST"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 1.0; - } - else if (wave_type == "SQUARE"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; - } - else if (wave_type == "RAMP"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; - } - else if (wave_type == "SINE"){ - static const double tau = 2*std::acos(-1.0); - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = std::sin((tau*i)/wave_table_len); - } - else throw std::runtime_error("unknown waveform type: " + wave_type); - - //compute i and q pairs with 90% offset and scale to amplitude - for (size_t i = 0; i < wave_table_len; i++){ - const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; - _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); - } - } - - inline std::complex<float> operator()(const size_t index) const{ - return _wave_table[index % wave_table_len]; - } - -private: - std::vector<std::complex<float> > _wave_table; -}; - -/*********************************************************************** * Main function **********************************************************************/ int UHD_SAFE_MAIN(int argc, char *argv[]){ diff --git a/host/examples/txrx_loopback_to_file.cpp b/host/examples/txrx_loopback_to_file.cpp index efa23c410..7dc3bd9c2 100644 --- a/host/examples/txrx_loopback_to_file.cpp +++ b/host/examples/txrx_loopback_to_file.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "wavetable.hpp" #include <uhd/types/tune_request.hpp> #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> @@ -31,7 +32,6 @@ #include <iostream> #include <fstream> #include <csignal> -#include <cmath> namespace po = boost::program_options; @@ -61,51 +61,6 @@ std::string generate_out_filename(const std::string &base_fn, size_t n_names, si return base_fn_fp.string(); } -/*********************************************************************** - * Waveform generators - **********************************************************************/ -static const size_t wave_table_len = 8192; - -class wave_table_class{ -public: - wave_table_class(const std::string &wave_type, const float ampl): - _wave_table(wave_table_len) - { - //compute real wave table with 1.0 amplitude - std::vector<double> real_wave_table(wave_table_len); - if (wave_type == "CONST"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 1.0; - } - else if (wave_type == "SQUARE"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; - } - else if (wave_type == "RAMP"){ - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; - } - else if (wave_type == "SINE"){ - static const double tau = 2*std::acos(-1.0); - for (size_t i = 0; i < wave_table_len; i++) - real_wave_table[i] = std::sin((tau*i)/wave_table_len); - } - else throw std::runtime_error("unknown waveform type: " + wave_type); - - //compute i and q pairs with 90% offset and scale to amplitude - for (size_t i = 0; i < wave_table_len; i++){ - const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; - _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); - } - } - - inline std::complex<float> operator()(const size_t index) const{ - return _wave_table[index % wave_table_len]; - } - -private: - std::vector<std::complex<float> > _wave_table; -}; /*********************************************************************** * transmit_worker function diff --git a/host/examples/wavetable.hpp b/host/examples/wavetable.hpp new file mode 100644 index 000000000..d7ffc8406 --- /dev/null +++ b/host/examples/wavetable.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2010-2012,2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <string> +#include <cmath> +#include <complex> +#include <vector> +#include <stdexcept> + +static const size_t wave_table_len = 8192; + +class wave_table_class{ +public: + wave_table_class(const std::string &wave_type, const float ampl): + _wave_table(wave_table_len) + { + //compute real wave table with 1.0 amplitude + std::vector<double> real_wave_table(wave_table_len); + if (wave_type == "CONST"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 1.0; + } + else if (wave_type == "SQUARE"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = (i < wave_table_len/2)? 0.0 : 1.0; + } + else if (wave_type == "RAMP"){ + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = 2.0*i/(wave_table_len-1) - 1.0; + } + else if (wave_type == "SINE"){ + static const double tau = 2*std::acos(-1.0); + for (size_t i = 0; i < wave_table_len; i++) + real_wave_table[i] = std::sin((tau*i)/wave_table_len); + } + else throw std::runtime_error("unknown waveform type: " + wave_type); + + //compute i and q pairs with 90% offset and scale to amplitude + for (size_t i = 0; i < wave_table_len; i++){ + const size_t q = (i+(3*wave_table_len)/4)%wave_table_len; + _wave_table[i] = std::complex<float>(ampl*real_wave_table[i], ampl*real_wave_table[q]); + } + } + + inline std::complex<float> operator()(const size_t index) const{ + return _wave_table[index % wave_table_len]; + } + +private: + std::vector<std::complex<float> > _wave_table; +}; + diff --git a/host/include/CMakeLists.txt b/host/include/CMakeLists.txt index 780213918..8b1e6bc05 100644 --- a/host/include/CMakeLists.txt +++ b/host/include/CMakeLists.txt @@ -20,4 +20,11 @@ CONFIGURE_FILE( ${CMAKE_CURRENT_BINARY_DIR}/config.h ) +IF(ENABLE_C_API) + UHD_INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/uhd.h + DESTINATION ${INCLUDE_DIR} + COMPONENT headers + ) +ENDIF(ENABLE_C_API) + ADD_SUBDIRECTORY(uhd) diff --git a/host/include/uhd.h b/host/include/uhd.h new file mode 100644 index 000000000..8647e25e4 --- /dev/null +++ b/host/include/uhd.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_UHD_H +#define INCLUDED_UHD_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#include <uhd/types/metadata.h> +#include <uhd/types/ranges.h> +#include <uhd/types/sensors.h> +#include <uhd/types/string_vector.h> +#include <uhd/types/tune_request.h> +#include <uhd/types/tune_result.h> +#include <uhd/types/usrp_info.h> + +#include <uhd/usrp/dboard_eeprom.h> +#include <uhd/usrp/mboard_eeprom.h> +#include <uhd/usrp/subdev_spec.h> +#include <uhd/usrp/usrp.h> + +#include <uhd/usrp_clock/usrp_clock.h> + +#include <uhd/utils/thread_priority.h> + +#endif /* INCLUDED_UHD_H */ diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index f6123aa90..083ec4951 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011,2013-2014 Ettus Research LLC +# Copyright 2010-2011,2013-2015 Ettus Research LLC # # 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 @@ -40,3 +40,12 @@ UHD_INSTALL(FILES DESTINATION ${INCLUDE_DIR}/uhd COMPONENT headers ) + +IF(ENABLE_C_API) + UHD_INSTALL(FILES + config.h + error.h + DESTINATION ${INCLUDE_DIR}/uhd + COMPONENT headers + ) +ENDIF(ENABLE_C_API) diff --git a/host/include/uhd/config.h b/host/include/uhd/config.h new file mode 100644 index 000000000..1d6cefcc0 --- /dev/null +++ b/host/include/uhd/config.h @@ -0,0 +1,82 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_CONFIG_H +#define INCLUDED_UHD_CONFIG_H + +#ifdef _MSC_VER +// Bring in "and", "or", and "not" +#include <iso646.h> + +// Define ssize_t +#include <stddef.h> +typedef ptrdiff_t ssize_t; + +#endif /* _MSC_VER */ + +// Define cross-platform macros +#if defined(_MSC_VER) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE __forceinline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x +#elif defined(__MINGW32__) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE inline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x __attribute__((unused)) +#elif defined(__GNUG__) && __GNUG__ >= 4 + #define UHD_EXPORT __attribute__((visibility("default"))) + #define UHD_IMPORT __attribute__((visibility("default"))) + #define UHD_INLINE inline __attribute__((always_inline)) + #define UHD_DEPRECATED __attribute__((deprecated)) + #define UHD_ALIGNED(x) __attribute__((aligned(x))) + #define UHD_UNUSED(x) x __attribute__((unused)) +#else + #define UHD_EXPORT + #define UHD_IMPORT + #define UHD_INLINE inline + #define UHD_DEPRECATED + #define UHD_ALIGNED(x) + #define UHD_UNUSED(x) x +#endif + +// API declaration macro +#ifdef UHD_DLL_EXPORTS + #define UHD_API UHD_EXPORT +#else + #define UHD_API UHD_IMPORT +#endif // UHD_DLL_EXPORTS + +// Platform defines for conditional code: +// Taken from boost/config/select_platform_config.hpp, +// However, we define macros, not strings, for platforms. +#if defined(linux) || defined(__linux) || defined(__linux__) + #define UHD_PLATFORM_LINUX +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define UHD_PLATFORM_WIN32 +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + #define UHD_PLATFORM_MACOS +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define UHD_PLATFORM_BSD +#endif + +#endif /* INCLUDED_UHD_CONFIG_H */ diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 7ecc4924a..8939cd773 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2014-2015 Ettus Research LLC // // 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 @@ -56,6 +56,13 @@ typedef ptrdiff_t ssize_t; #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) #define UHD_UNUSED(x) x +#elif defined(__MINGW32__) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE inline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x __attribute__((unused)) #elif defined(__GNUG__) && __GNUG__ >= 4 #define UHD_EXPORT __attribute__((visibility("default"))) #define UHD_IMPORT __attribute__((visibility("default"))) diff --git a/host/include/uhd/convert.hpp b/host/include/uhd/convert.hpp index d740d80fb..e42123b20 100644 --- a/host/include/uhd/convert.hpp +++ b/host/include/uhd/convert.hpp @@ -63,12 +63,13 @@ namespace uhd{ namespace convert{ typedef int priority_type; //! Identify a conversion routine in the registry - struct id_type : boost::equality_comparable<id_type>{ + struct UHD_API id_type : boost::equality_comparable<id_type>{ std::string input_format; size_t num_inputs; std::string output_format; size_t num_outputs; std::string to_pp_string(void) const; + std::string to_string(void) const; }; //! Implement equality_comparable interface diff --git a/host/include/uhd/error.h b/host/include/uhd/error.h new file mode 100644 index 000000000..f0ac41d1f --- /dev/null +++ b/host/include/uhd/error.h @@ -0,0 +1,169 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_UHD_ERROR_H +#define INCLUDED_UHD_ERROR_H + +#include <stdlib.h> + +//! UHD error codes +/*! + * Each error code corresponds to a specific uhd::exception, with + * extra codes corresponding to a boost::exception, std::exception, + * and a catch-all for everything else. When an internal C++ function + * throws an exception, UHD converts it to one of these error codes + * to return on the C level. + */ +typedef enum { + + //! No error thrown. + UHD_ERROR_NONE = 0, + //! Invalid device arguments. + UHD_ERROR_INVALID_DEVICE = 1, + + //! See uhd::index_error. + UHD_ERROR_INDEX = 10, + //! See uhd::key_error. + UHD_ERROR_KEY = 11, + + //! See uhd::not_implemented_error. + UHD_ERROR_NOT_IMPLEMENTED = 20, + //! See uhd::usb_error. + UHD_ERROR_USB = 21, + + //! See uhd::io_error. + UHD_ERROR_IO = 30, + //! See uhd::os_error. + UHD_ERROR_OS = 31, + + //! See uhd::assertion_error. + UHD_ERROR_ASSERTION = 40, + //! See uhd::lookup_error. + UHD_ERROR_LOOKUP = 41, + //! See uhd::type_error. + UHD_ERROR_TYPE = 42, + //! See uhd::value_error. + UHD_ERROR_VALUE = 43, + //! See uhd::runtime_error. + UHD_ERROR_RUNTIME = 44, + //! See uhd::environment_error. + UHD_ERROR_ENVIRONMENT = 45, + //! See uhd::system_error. + UHD_ERROR_SYSTEM = 46, + //! See uhd::exception. + UHD_ERROR_EXCEPT = 47, + + //! A boost::exception was thrown. + UHD_ERROR_BOOSTEXCEPT = 60, + + //! A std::exception was thrown. + UHD_ERROR_STDEXCEPT = 70, + + //! An unknown error was thrown. + UHD_ERROR_UNKNOWN = 100 +} uhd_error; + +#ifdef __cplusplus +#include <uhd/config.hpp> +#include <uhd/exception.hpp> + +#include <boost/exception/diagnostic_information.hpp> + +#include <string> + +UHD_API uhd_error error_from_uhd_exception(const uhd::exception* e); + +UHD_API const std::string& get_c_global_error_string(); + +UHD_API void set_c_global_error_string(const std::string &msg); + +/*! + * This macro runs the given C++ code, and if there are any exceptions + * thrown, they are caught and converted to the corresponding UHD error + * code. + */ +#define UHD_SAFE_C(...) \ + try{ __VA_ARGS__ } \ + catch (const uhd::exception &e) { \ + set_c_global_error_string(e.what()); \ + return error_from_uhd_exception(&e); \ + } \ + catch (const boost::exception &e) { \ + set_c_global_error_string(boost::diagnostic_information(e)); \ + return UHD_ERROR_BOOSTEXCEPT; \ + } \ + catch (const std::exception &e) { \ + set_c_global_error_string(e.what()); \ + return UHD_ERROR_STDEXCEPT; \ + } \ + catch (...) { \ + set_c_global_error_string("Unrecognized exception caught."); \ + return UHD_ERROR_UNKNOWN; \ + } \ + set_c_global_error_string("None"); \ + return UHD_ERROR_NONE; + +/*! + * This macro runs the given C++ code, and if there are any exceptions + * thrown, they are caught and converted to the corresponding UHD error + * code. The error message is also saved into the given handle. + */ +#define UHD_SAFE_C_SAVE_ERROR(h, ...) \ + h->last_error.clear(); \ + try{ __VA_ARGS__ } \ + catch (const uhd::exception &e) { \ + set_c_global_error_string(e.what()); \ + h->last_error = e.what(); \ + return error_from_uhd_exception(&e); \ + } \ + catch (const boost::exception &e) { \ + set_c_global_error_string(boost::diagnostic_information(e)); \ + h->last_error = boost::diagnostic_information(e); \ + return UHD_ERROR_BOOSTEXCEPT; \ + } \ + catch (const std::exception &e) { \ + set_c_global_error_string(e.what()); \ + h->last_error = e.what(); \ + return UHD_ERROR_STDEXCEPT; \ + } \ + catch (...) { \ + set_c_global_error_string("Unrecognized exception caught."); \ + h->last_error = "Unrecognized exception caught."; \ + return UHD_ERROR_UNKNOWN; \ + } \ + h->last_error = "None"; \ + set_c_global_error_string("None"); \ + return UHD_ERROR_NONE; + +extern "C" { +#endif + +//! Return the last error string reported by UHD +/*! + * Functions that do not take in UHD structs/handles will place any error + * strings into a buffer that can be queried with this function. Functions that + * do take in UHD structs/handles will place their error strings in both locations. + */ +uhd_error uhd_get_last_error( + char* error_out, + size_t strbuffer_len +); +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_ERROR_H */ diff --git a/host/include/uhd/exception.hpp b/host/include/uhd/exception.hpp index 69e902fd5..a21106166 100644 --- a/host/include/uhd/exception.hpp +++ b/host/include/uhd/exception.hpp @@ -98,6 +98,14 @@ namespace uhd{ virtual void dynamic_throw(void) const; }; + struct UHD_API usb_error : runtime_error{ + int _code; + usb_error(int code, const std::string &what); + virtual unsigned code(void) const { return _code; }; + virtual usb_error *dynamic_clone(void) const; + virtual void dynamic_throw(void) const; + }; + struct UHD_API not_implemented_error : runtime_error{ not_implemented_error(const std::string &what); virtual unsigned code(void) const; diff --git a/host/include/uhd/image_loader.hpp b/host/include/uhd/image_loader.hpp new file mode 100644 index 000000000..5963c862f --- /dev/null +++ b/host/include/uhd/image_loader.hpp @@ -0,0 +1,90 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_IMAGE_LOADER_HPP +#define INCLUDED_UHD_IMAGE_LOADER_HPP + +#include <string> + +#include <boost/function.hpp> + +#include <uhd/config.hpp> +#include <uhd/types/device_addr.hpp> + +namespace uhd{ + +class UHD_API image_loader : boost::noncopyable{ + +public: + + typedef struct{ + uhd::device_addr_t args; + bool load_firmware; + bool load_fpga; + std::string firmware_path; + std::string fpga_path; + } image_loader_args_t; + + //! Signature of an image loading function + /*! + * This is the function signature for an image loading function. + * See the declaration of load() for the meaning of these arguments. + * + * This function must return true upon the end of a successful image load + * or false if no applicable device was found. It may only throw a runtime + * error under one of three conditions: + * + * * The function finds multiple devices that fit the user's arguments. + * * The function has already engaged with a specific device and + * something goes wrong. + * * The user gives arguments that unambiguously lead to a specific + * device and expect the default image(s) to be loaded, but the specific + * model of the device cannot be determined beyond a category. + */ + typedef boost::function<bool(const image_loader_args_t &)> loader_fcn_t; + + //! Register an image loader + /*! + * \param device_type the "type=foo" value given in an --args option + * \param loader_fcn the loader function for the given device + * \param recovery_instructions instructions on how to restore a device + */ + static void register_image_loader( + const std::string &device_type, + const loader_fcn_t &loader_fcn, + const std::string &recovery_instructions + ); + + //! Load firmware and/or FPGA onto a device + /*! + * \param image_loader_args arguments to pass into image loading function + */ + static bool load(const image_loader_args_t &image_loader_args); + + //! Get the instructions on how to recovery a particular device + /*! + * These instructions should be queried if the user interrupts an image loading + * session, as this will likely leave the device in an unstable state. + * \param device_type the "type=foo" value given in an --args option + * \return recoverying instructions + */ + static std::string get_recovery_instructions(const std::string &device_type); +}; + +} + +#endif /* INCLUDED_UHD_IMAGE_LOADER_HPP */ diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 2118674c6..623c179e9 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2014 Ettus Research LLC # # 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 @@ -15,11 +15,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # - UHD_INSTALL(FILES bounded_buffer.hpp bounded_buffer.ipp buffer_pool.hpp + chdr.hpp if_addrs.hpp udp_constants.hpp udp_simple.hpp diff --git a/host/include/uhd/transport/chdr.hpp b/host/include/uhd/transport/chdr.hpp new file mode 100644 index 000000000..5e8cd58a9 --- /dev/null +++ b/host/include/uhd/transport/chdr.hpp @@ -0,0 +1,113 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_CHDR_HPP +#define INCLUDED_UHD_TRANSPORT_CHDR_HPP + +#include <uhd/transport/vrt_if_packet.hpp> + +namespace uhd{ namespace transport{ namespace vrt{ + +/*! \brief CVITA/CHDR related function + * + * See \ref rtp_chdr for details on the CVITA/CHDR protocol. + * + * All packers take the host format into account. Choose the _le functions + * if the transport uses little endian format (e.g. PCIe) and the _be + * functions if the transport uses big endian format (e.g. Ethernet). + * + * Note 1: All packers assume there to be enough space at the address + * provided by \p packet_buff. See also \ref vrt_pack_contract. + * + * Note 2: All these packers assume the following options without checking them: + * - `if_packet_info.link_type == LINK_TYPE_CHDR` + * - `if_packet_info.has_cid == false` + * - `if_packet_info.has_sid == true` + * - `if_packet_info.has_tsi == false` + * - `if_packet_info.has_tlr == false` + * This relaxes some of \ref vrt_pack_contract, but adds the additional + * constraint that the input data must be CHDR. + * + * In the unpacker, these values will be set accordingly. + */ +namespace chdr{ + + //! The maximum number of 64-bit words in a CVITA header + static const size_t max_if_hdr_words64 = 2; // CHDR + tsf (fractional timestamp) + + /*! + * Pack a CHDR header from metadata (big endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_pack_be( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Unpack a CHDR header to metadata (big endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Pack a CHDR header from metadata (little endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_pack_le( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + + /*! + * Unpack a CHDR header to metadata (little endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ + UHD_API void if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info + ); + +} //namespace chdr + +}}} //namespace uhd::transport::vrt + +#endif /* INCLUDED_UHD_TRANSPORT_CHDR_HPP */ + diff --git a/host/include/uhd/transport/nirio/nirio_driver_iface.h b/host/include/uhd/transport/nirio/nirio_driver_iface.h index 10df8e04e..6804f6171 100644 --- a/host/include/uhd/transport/nirio/nirio_driver_iface.h +++ b/host/include/uhd/transport/nirio/nirio_driver_iface.h @@ -24,57 +24,84 @@ #include <uhd/transport/nirio/status.h> #include <uhd/config.hpp> #if defined(UHD_PLATFORM_WIN32) - #include <Windows.h> - #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union - #include <WinIoCtl.h> - #pragma warning(default:4201) + #include <windows.h> + #ifdef _MSC_VER + #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union + #endif + #include <winioctl.h> + #ifdef _MSC_VER + #pragma warning(default:4201) + #endif #elif defined(UHD_PLATFORM_MACOS) #include <IOKit/IOKitLib.h> +#elif defined(UHD_PLATFORM_LINUX) + #include <linux/ioctl.h> #endif -// CTL_CODE macro for non-win OSes -#ifndef UHD_PLATFORM_WIN32 - #define CTL_CODE(a,controlCode,b,c) (controlCode) +#if __GNUC__ + typedef uint64_t aligned_uint64_t __attribute__ ((aligned(8))); +#else + typedef uint64_t aligned_uint64_t; #endif -namespace nirio_driver_iface { +//IOCTL Definitions + +#if defined(UHD_PLATFORM_WIN32) + + #define IOCTL_ACCESS_ANY (FILE_ANY_ACCESS) + #define IOCTL_ACCESS_READ (FILE_READ_ACCESS) + #define IOCTL_ACCESS_WRITE (FILE_WRITE_ACCESS) + #define IOCTL_ACCESS_RW (FILE_READ_ACCESS | FILE_WRITE_ACCESS) + + #define IOCTL(type, function, access) \ + CTL_CODE((0x8000+type), (0x800+function), METHOD_BUFFERED, access) + +#elif defined(UHD_PLATFORM_MACOS) + + #define IOCTL_ACCESS_ANY (0U) + #define IOCTL_ACCESS_READ (1U) + #define IOCTL_ACCESS_WRITE (2U) + #define IOCTL_ACCESS_RW (3U) + + #define IOCTL(type, function, access) \ + (((access & 0x0003) << 30) | \ + ((type & 0x00FF) << 16) | \ + ((function & 0xFFFF) << 0)) + +#elif defined(UHD_PLATFORM_LINUX) + + #define IOCTL_ACCESS_ANY (_IOC_NONE) + #define IOCTL_ACCESS_READ (_IOC_READ) + #define IOCTL_ACCESS_WRITE (_IOC_WRITE) + #define IOCTL_ACCESS_RW (_IOC_READ | _IOC_WRITE) + + struct nirio_ioctl_block_t { + aligned_uint64_t in_buf; + aligned_uint64_t out_buf; + uint32_t in_buf_len; + uint32_t out_buf_len; + uint32_t bytes_returned; + uint32_t padding; + }; + + #define IOCTL(type, function, access) \ + _IOC(access, type, function, sizeof(nirio_ioctl_block_t)) -const uint32_t NIRIO_IOCTL_BASE = 0x800; - -const uint32_t NIRIO_IOCTL_SYNCOP = - CTL_CODE(FILE_DEVICE_UNKNOWN, - NIRIO_IOCTL_BASE + 4, - METHOD_OUT_DIRECT, - FILE_READ_DATA | FILE_WRITE_DATA); - ///< The synchronous operation code. Note: We - /// must use METHOD_OUT_DIRECT on the syncOp() - /// IOCTL to ensure the contents of the output - /// block are available in the kernel. - -const uint32_t NIRIO_IOCTL_GET_IFACE_NUM = - CTL_CODE(FILE_DEVICE_UNKNOWN, - NIRIO_IOCTL_BASE + 6, - METHOD_BUFFERED, - FILE_READ_DATA); ///< Get the interface number for a device - -const uint32_t NIRIO_IOCTL_GET_SESSION = - CTL_CODE(FILE_DEVICE_UNKNOWN, - NIRIO_IOCTL_BASE + 8, - METHOD_BUFFERED, - FILE_READ_ACCESS); ///< Gets a previously opened session to a device - -const uint32_t NIRIO_IOCTL_POST_OPEN = - CTL_CODE(FILE_DEVICE_UNKNOWN, - NIRIO_IOCTL_BASE + 9, - METHOD_BUFFERED, - FILE_READ_ACCESS); ///< Called after opening a session - -const uint32_t NIRIO_IOCTL_PRE_CLOSE = - CTL_CODE(FILE_DEVICE_UNKNOWN, - NIRIO_IOCTL_BASE + 10, - METHOD_BUFFERED, - FILE_READ_ACCESS); ///< Called before closing a session +#else + #define IOCTL_ACCESS_ANY (0U) + #define IOCTL_ACCESS_READ (1U) + #define IOCTL_ACCESS_WRITE (2U) + #define IOCTL_ACCESS_RW (3U) + + #define IOCTL(type, function, access) \ + (((access & 0x0003) << 30) | \ + ((type & 0x00FF) << 16) | \ + ((function & 0xFFFF) << 0)) + +#endif + +namespace nirio_driver_iface { //Device handle definition #if defined(UHD_PLATFORM_LINUX) @@ -83,7 +110,7 @@ const uint32_t NIRIO_IOCTL_PRE_CLOSE = typedef HANDLE rio_dev_handle_t; #elif defined(UHD_PLATFORM_MACOS) typedef io_connect_t rio_dev_handle_t; -#else //Unsupported platforms +#else typedef int rio_dev_handle_t; #endif static const rio_dev_handle_t INVALID_RIO_HANDLE = ((rio_dev_handle_t)-1); diff --git a/host/include/uhd/transport/nirio/nirio_fifo.h b/host/include/uhd/transport/nirio/nirio_fifo.h index cd471474d..a0cd7149f 100644 --- a/host/include/uhd/transport/nirio/nirio_fifo.h +++ b/host/include/uhd/transport/nirio/nirio_fifo.h @@ -59,8 +59,8 @@ public: inline const std::string& get_name() const { return _name; } inline uint32_t get_channel() const { return _fifo_channel; } - inline uint32_t get_direction() const { return _fifo_direction; } - inline uint32_t get_scalar_type() const { return _datatype_info.scalar_type; } + inline fifo_direction_t get_direction() const { return _fifo_direction; } + inline nirio_scalar_type_t get_scalar_type() const { return _datatype_info.scalar_type; } nirio_status start(); diff --git a/host/include/uhd/transport/nirio/nirio_quirks.h b/host/include/uhd/transport/nirio/nirio_quirks.h index 8e58f80f1..45ef40394 100644 --- a/host/include/uhd/transport/nirio/nirio_quirks.h +++ b/host/include/uhd/transport/nirio/nirio_quirks.h @@ -35,8 +35,8 @@ public: nirio_quirks() : _tx_stream_count(0) { } - UHD_INLINE void register_tx_streams(const uint32_t tx_stream_indices[]) { - for (size_t i = 0; i < sizeof(tx_stream_indices)/sizeof(*tx_stream_indices); i++) { + UHD_INLINE void register_tx_streams(const uint32_t tx_stream_indices[], size_t size) { + for (size_t i = 0; i < size; i++) { _tx_stream_fifo_indices.insert(tx_stream_indices[i]); } } diff --git a/host/include/uhd/transport/nirio/niriok_proxy.h b/host/include/uhd/transport/nirio/niriok_proxy.h index 07bcb59b6..bb112dd8a 100644 --- a/host/include/uhd/transport/nirio/niriok_proxy.h +++ b/host/include/uhd/transport/nirio/niriok_proxy.h @@ -145,29 +145,9 @@ namespace uhd { namespace niusrprio class UHD_API niriok_proxy : public boost::noncopyable { public: - struct nirio_ioctl_packet_t { - nirio_ioctl_packet_t( - void* const _outBuf, - const uint32_t _outSize, - const int32_t _statusCode) - { - outBuf._64BitField = 0; - outBuf.pointer = _outBuf; - outSize = _outSize; - statusCode = _statusCode; - }; - - union { - void* pointer; - uint64_t _64BitField; - } outBuf; - - uint32_t outSize; - int32_t statusCode; - }; - typedef boost::shared_ptr<niriok_proxy> sptr; - - static sptr make_and_open(const std::string& interface_path); + typedef boost::shared_ptr<niriok_proxy> sptr; + + static sptr make_and_open(const std::string& interface_path); niriok_proxy(); virtual ~niriok_proxy(); @@ -180,9 +160,6 @@ namespace uhd { namespace niusrprio uint32_t get_interface_num() { return _interface_num; } - virtual nirio_status get_cached_session( - uint32_t& session) = 0; - virtual nirio_status get_version( nirio_version_t type, uint32_t& major, diff --git a/host/include/uhd/transport/nirio/niriok_proxy_impl_v1.h b/host/include/uhd/transport/nirio/niriok_proxy_impl_v1.h index 173d43ed6..a10d1aaff 100644 --- a/host/include/uhd/transport/nirio/niriok_proxy_impl_v1.h +++ b/host/include/uhd/transport/nirio/niriok_proxy_impl_v1.h @@ -33,341 +33,8 @@ namespace uhd { namespace niusrprio are not compatible with NI-RIO 14.0 and later. */ - class UHD_API niriok_proxy_impl_v1 : virtual public niriok_proxy { + class UHD_API niriok_proxy_impl_v1 : virtual public niriok_proxy { public: - - // ------------------------------- - // Function Codes: defined as integers rather than enums because they - // are going to be carried accross boundaries so size matters - - struct NIRIO_FUNC - { - static const uint32_t GET32 = 0x00000001; - static const uint32_t SET32 = 0x00000002; - static const uint32_t SET_DRIVER_CONFIG = 0x00000007; - static const uint32_t FIFO = 0x00000008; - static const uint32_t IO = 0x0000000A; - static const uint32_t FIFO_STOP_ALL = 0x0000000C; - static const uint32_t ADD_RESOURCE = 0x0000000D; - static const uint32_t GET_STRING = 0x0000000E; - static const uint32_t SET_STRING = 0x0000000F; - static const uint32_t DOWNLOAD = 0x00000013; - static const uint32_t RESET = 0x00000014; - }; - - struct NIRIO_RESOURCE - { - static const uint32_t INPUT_FIFO = 0xD0000001; - static const uint32_t OUTPUT_FIFO = 0xD0000002; - }; - - struct NIRIO_FIFO - { - static const uint32_t CONFIGURE = 0x80000001; - static const uint32_t START = 0x80000002; - static const uint32_t STOP = 0x80000003; - static const uint32_t READ = 0x80000004; - static const uint32_t WRITE = 0x80000005; - static const uint32_t WAIT = 0x80000006; - static const uint32_t GRANT = 0x80000007; - }; - - struct NIRIO_IO - { - static const uint32_t POKE64 = 0xA0000005; - static const uint32_t POKE32 = 0xA0000006; - static const uint32_t POKE16 = 0xA0000007; - static const uint32_t POKE8 = 0xA0000008; - static const uint32_t PEEK64 = 0xA0000009; - static const uint32_t PEEK32 = 0xA000000A; - static const uint32_t PEEK16 = 0xA000000B; - static const uint32_t PEEK8 = 0xA000000C; - static const uint32_t READ_BLOCK = 0xA000000D; - static const uint32_t WRITE_BLOCK = 0xA000000E; - static const uint32_t GET_IO_WINDOW = 0xA000000F; - static const uint32_t GET_IO_WINDOW_SIZE = 0xA0000010; - }; - - struct nirio_syncop_in_params_t - { - uint32_t function; - uint32_t subfunction; - - union - { - struct - { - uint32_t attribute; - uint32_t value; - } attribute32; - - struct - { - uint32_t attribute; - uint64_t value; - } attribute64; - - struct - { - uint32_t attribute; - } attributeStr; - - struct - { - uint32_t attribute; - } download; - - union - { - struct - { - uint32_t reserved_field_0_0_0; - } reserved_field_0_0; - struct - { - uint32_t reserved_field_0_1_0; - uint32_t reserved_field_0_1_1; - } reserved_field_0_1; - struct - { - uint32_t reserved_field_0_2_0; - } reserved_field_0_2; - } reserved_field_0; - - union - { - struct - { - uint32_t channel; - uint32_t baseAddress; - uint32_t depthInSamples; - uint32_t version; - } fifo; - struct - { - uint32_t channel; - uint32_t baseAddress; - uint32_t depthInSamples; - uint32_t version; - uint32_t scalarType; - uint32_t bitWidth; - } fifoWithDataType; - struct - { - uint64_t rangeBaseAddress; - uint32_t rangeSizeInBytes; - uint32_t rangeAttribute; - } atomic; // obsolete - } add; - - struct - { - uint32_t channel; - - union - { - struct - { - uint32_t requestedDepth; - uint8_t requiresActuals; - } config; - struct - { - uint32_t timeout; - } read; - struct - { - uint32_t timeout; - uint32_t scalarType; - uint32_t bitWidth; - } readWithDataType; - struct - { - uint32_t timeout; - } write; - struct - { - uint32_t timeout; - uint32_t scalarType; - uint32_t bitWidth; - } writeWithDataType; - struct - { - uint32_t elementsRequested; - uint32_t scalarType; - uint32_t bitWidth; - uint32_t timeout; - uint8_t output; - } wait; - struct - { - uint32_t elements; - } grant; - } op; - } fifo; - - struct - { - uint64_t reserved_field_1_0; - uint32_t reserved_field_1_1; - uint32_t reserved_field_1_2; - } reserved_field_1; // Obsolete - - struct - { - uint32_t offset; - union - { - uint64_t value64; - uint32_t value32; - uint16_t value16; - uint8_t value8; - } value; - union - { - uint32_t sizeToMap; - } memoryMappedIoWindow; - } io; - - struct - { - uint32_t reserved_field_2_0; - uint32_t reserved_field_2_1; - } reserved_field_2; - - struct - { - uint32_t reserved_field_3_0; - } reserved_field_3; - - union - { - struct - { - uint32_t reserved_field_4_0; - int32_t reserved_field_4_1; - } wait; - } reserved_field_4; - - } params; - - uint32_t inbufByteLen; - - union - { - const void* pointer; - uint64_t _64BitField; - } inbuf; - }; - - static inline void init_syncop_in_params(nirio_syncop_in_params_t& param, const void* const buf, const uint32_t len) - { - param.inbuf._64BitField = 0; - param.inbuf.pointer = buf; - param.inbufByteLen = len; - } - - - struct nirio_syncop_out_params_t - { - union - { - struct - { - uint32_t value; - } attribute32; - - struct - { - uint64_t value; - } attribute64; - - union - { - struct - { - uint32_t reserved_field_0_0; - } enable; - } reserved_field_0; - - struct - { - union - { - struct - { - uint32_t actualDepth; - uint32_t actualSize; - } config; - struct - { - uint32_t numberRead; - uint32_t numberRemaining; - } read; - struct - { - uint32_t numberRemaining; - } write; - struct - { - union - { - void* pointer; - uint64_t _64BitField; - } elements; - } wait; - } op; - } fifo; - - struct - { - union - { - union - { - uint64_t value64; - uint32_t value32; - uint16_t value16; - uint8_t value8; - } value; - union - { - void* memoryMappedAddress; - uint64_t _64BitField; - } memoryMappedIoWindow; - union - { - uint32_t size; - } memoryMappedIoWindowSize; - }; - } io; - - uint32_t stringLength; - - struct - { - uint32_t reserved_field_1_0; - } reserved_field_1; - - } params; - - uint32_t outbufByteLen; - - union - { - void* pointer; - uint64_t _64BitField; - } outbuf; - }; - - - static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void* buf, uint32_t len) - { - param.outbuf._64BitField = 0; - param.outbuf.pointer = buf; - param.outbufByteLen = len; - } - niriok_proxy_impl_v1(); virtual ~niriok_proxy_impl_v1(); @@ -377,9 +44,6 @@ namespace uhd { namespace niusrprio virtual nirio_status reset(); - virtual nirio_status get_cached_session( - uint32_t& session); - virtual nirio_status get_version( nirio_version_t type, uint32_t& major, @@ -477,7 +141,6 @@ namespace uhd { namespace niusrprio size_t writeBufferLength, void *readBuffer, size_t readBufferLength); - }; }} diff --git a/host/include/uhd/transport/nirio/niriok_proxy_impl_v2.h b/host/include/uhd/transport/nirio/niriok_proxy_impl_v2.h index 333ab4348..c0183502a 100644 --- a/host/include/uhd/transport/nirio/niriok_proxy_impl_v2.h +++ b/host/include/uhd/transport/nirio/niriok_proxy_impl_v2.h @@ -24,12 +24,6 @@ #include <uhd/transport/nirio/nirio_quirks.h> #include <uhd/transport/nirio/niriok_proxy.h> -#if __GNUC__ - typedef uint64_t tAlignedU64 __attribute__ ((aligned(8))); -#else - typedef uint64_t tAlignedU64; -#endif - namespace uhd { namespace niusrprio { /* @@ -39,267 +33,8 @@ namespace uhd { namespace niusrprio are not compatible with NI-RIO versions older than 14.0. */ - #define IOCTL(type, function, access) \ - CTL_CODE((0x8000+type), (0x800+function), METHOD_BUFFERED, access) - - #define IOCTL_ACCESS_ANY (FILE_ANY_ACCESS) - #define IOCTL_ACCESS_READ (FILE_READ_ACCESS) - #define IOCTL_ACCESS_WRITE (FILE_WRITE_ACCESS) - #define IOCTL_ACCESS_RW (FILE_READ_ACCESS | FILE_WRITE_ACCESS) - - #define IOCTL_TRANSPORT_GET32 IOCTL(0, 0, IOCTL_ACCESS_READ) - #define IOCTL_TRANSPORT_SET32 IOCTL(0, 1, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_GET_STRING IOCTL(0, 2, IOCTL_ACCESS_READ) - #define IOCTL_TRANSPORT_SET_STRING IOCTL(0, 3, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_RESET IOCTL(1, 1, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_ADD_INPUT_FIFO_RESOURCE IOCTL(2, 0, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_ADD_OUTPUT_FIFO_RESOURCE IOCTL(2, 1, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_SET_DEVICE_CONFIG IOCTL(2, 3, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_FIFO_CONFIG IOCTL(4, 0, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_FIFO_START IOCTL(4, 1, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_FIFO_STOP IOCTL(4, 2, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_FIFO_READ IOCTL(4, 3, IOCTL_ACCESS_READ) - #define IOCTL_TRANSPORT_FIFO_WRITE IOCTL(4, 4, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_FIFO_WAIT IOCTL(4, 5, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_FIFO_GRANT IOCTL(4, 6, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_FIFO_STOP_ALL IOCTL(4, 7, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_PEEK64 IOCTL(5, 2, IOCTL_ACCESS_READ) - #define IOCTL_TRANSPORT_PEEK32 IOCTL(5, 3, IOCTL_ACCESS_READ) - #define IOCTL_TRANSPORT_POKE64 IOCTL(5, 6, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_POKE32 IOCTL(5, 7, IOCTL_ACCESS_WRITE) - #define IOCTL_TRANSPORT_POST_OPEN IOCTL(8, 0, IOCTL_ACCESS_ANY) - #define IOCTL_TRANSPORT_PRE_CLOSE IOCTL(8, 1, IOCTL_ACCESS_ANY) - - typedef struct { - nirio_scalar_type_t scalarType; - nirio_u32_t bitWidth; - nirio_i32_t integerWordLength; - } nirio_fifo_data_type_t; - class UHD_API niriok_proxy_impl_v2 : virtual public niriok_proxy { public: - typedef struct in_transport_get32 - { - nirio_device_attribute32_t attribute; - int32_t status; - } in_transport_get32_t; - typedef struct out_transport_get32 - { - uint32_t retVal__; - int32_t status; - } out_transport_get32_t; - typedef struct in_transport_set32 - { - nirio_device_attribute32_t attribute; - uint32_t value; - int32_t status; - } in_transport_set32_t; - typedef struct out_transport_set32 - { - int32_t status; - } out_transport_set32_t; - typedef struct out_transport_get_string - { - uint32_t stringLen; - int32_t status; - } out_transport_get_string_t; - typedef struct out_transport_set_string - { - int32_t status; - } out_transport_set_string_t; - typedef struct in_transport_reset - { - int32_t status; - } in_transport_reset_t; - typedef struct out_transport_reset - { - int32_t status; - } out_transport_reset_t; - typedef struct in_transport_add_input_fifo_resource - { - uint32_t channel; - uint32_t baseAddress; - uint32_t depthInSamples; - nirio_fifo_data_type_t dataType; - uint32_t version; - int32_t status; - } in_transport_add_input_fifo_resource_t; - typedef struct out_transport_addInputFifo_resource - { - int32_t status; - } out_transport_add_input_fifo_resource_t; - typedef struct in_transport_addOutputFifo_resource - { - uint32_t channel; - uint32_t baseAddress; - uint32_t depthInSamples; - nirio_fifo_data_type_t dataType; - uint32_t version; - int32_t status; - } in_transport_add_output_fifo_resource_t; - typedef struct out_transport_addOutputFifo_resource - { - int32_t status; - } out_transport_add_output_fifo_resource_t; - typedef struct in_transport_setDevice_config - { - uint32_t attribute; - int32_t status; - } in_transport_set_device_config_t; - typedef struct out_transport_setDevice_config - { - int32_t status; - } out_transport_set_device_config_t; - typedef struct in_transport_fifo_config - { - uint32_t channel; - tAlignedU64 requestedDepth; - int32_t status; - } in_transport_fifo_config_t; - typedef struct out_transport_fifo_config - { - tAlignedU64 actualDepth; - tAlignedU64 actualSize; - int32_t status; - } out_transport_fifo_config_t; - typedef struct in_transport_fifo_start - { - uint32_t channel; - int32_t status; - } in_transport_fifo_start_t; - typedef struct out_transport_fifo_start - { - int32_t status; - } out_transport_fifo_start_t; - typedef struct in_transport_fifo_stop - { - uint32_t channel; - int32_t status; - } in_transport_fifo_stop_t; - typedef struct out_transport_fifo_stop - { - int32_t status; - } out_transport_fifo_stop_t; - typedef struct in_transport_fifo_read - { - uint32_t channel; - tAlignedU64 buf; - uint32_t numberElements; - nirio_fifo_data_type_t dataType; - uint32_t timeout; - int32_t status; - } in_transport_fifo_read_t; - typedef struct out_transport_fifo_read - { - uint32_t read; - uint32_t remaining; - int32_t status; - } out_transport_fifo_read_t; - typedef struct in_transport_fifo_write - { - uint32_t channel; - tAlignedU64 buf; - uint32_t numberElements; - nirio_fifo_data_type_t dataType; - uint32_t timeout; - int32_t status; - } in_transport_fifo_write_t; - typedef struct out_transport_fifo_write - { - uint32_t remaining; - int32_t status; - } out_transport_fifo_write_t; - typedef struct in_transport_fifo_wait - { - uint32_t channel; - tAlignedU64 elementsRequested; - nirio_fifo_data_type_t dataType; - bool output; - uint32_t timeout; - int32_t status; - } in_transport_fifo_wait_t; - typedef struct out_transport_fifo_wait - { - tAlignedU64 elements; - tAlignedU64 elementsAcquired; - tAlignedU64 elementsRemaining; - int32_t status; - } out_transport_fifo_wait_t; - typedef struct in_transport_fifo_grant - { - uint32_t channel; - tAlignedU64 elements; - int32_t status; - } in_transport_fifo_grant_t; - typedef struct out_transport_fifo_grant - { - int32_t status; - } out_transport_fifo_grant_t; - typedef struct in_transport_fifoStop_all - { - int32_t status; - } in_transport_fifo_stop_all_t; - typedef struct out_transport_fifoStop_all - { - int32_t status; - } out_transport_fifo_stop_all_t; - typedef struct in_transport_peek64 - { - uint32_t offset; - int32_t status; - } in_transport_peek64_t; - typedef struct out_transport_peek64 - { - tAlignedU64 retVal__; - int32_t status; - } out_transport_peek64_t; - typedef struct in_transport_peek32 - { - uint32_t offset; - int32_t status; - } in_transport_peek32_t; - typedef struct out_transport_peek32 - { - uint32_t retVal__; - int32_t status; - } out_transport_peek32_t; - typedef struct in_transport_poke64 - { - uint32_t offset; - tAlignedU64 value; - int32_t status; - } in_transport_poke64_t; - typedef struct out_transport_poke64 - { - int32_t status; - } out_transport_poke64_t; - typedef struct in_transport_poke32 - { - uint32_t offset; - uint32_t value; - int32_t status; - } in_transport_poke32_t; - typedef struct out_transport_poke32 - { - int32_t status; - } out_transport_poke32_t; - typedef struct in_transport_post_open - { - int32_t status; - } in_transport_post_open_t; - typedef struct out_transport_post_open - { - int32_t status; - } out_transport_post_open_t; - typedef struct in_transport_pre_close - { - int32_t status; - } in_transport_pre_close_t; - typedef struct out_transport_pre_close - { - int32_t status; - } out_transport_pre_close_t; - niriok_proxy_impl_v2(); virtual ~niriok_proxy_impl_v2(); @@ -309,9 +44,6 @@ namespace uhd { namespace niusrprio virtual nirio_status reset(); - virtual nirio_status get_cached_session( - uint32_t& session); - virtual nirio_status get_version( nirio_version_t type, uint32_t& major, @@ -402,7 +134,6 @@ namespace uhd { namespace niusrprio protected: // protected close function that doesn't acquire synchronization lock virtual void _close(); - }; }} diff --git a/host/include/uhd/transport/tcp_zero_copy.hpp b/host/include/uhd/transport/tcp_zero_copy.hpp index 52e94fab9..ac282d8a2 100644 --- a/host/include/uhd/transport/tcp_zero_copy.hpp +++ b/host/include/uhd/transport/tcp_zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 Ettus Research LLC // // 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 @@ -32,7 +32,7 @@ namespace uhd{ namespace transport{ */ struct UHD_API tcp_zero_copy : public virtual zero_copy_if { - virtual ~tcp_zero_copy(void) = 0; + virtual ~tcp_zero_copy(void); /*! * Make a new zero copy TCP transport: diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index fdea9e2be..bf122f549 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -41,6 +41,7 @@ namespace uhd { namespace transport { class UHD_API usb_device_handle : boost::noncopyable { public: typedef boost::shared_ptr<usb_device_handle> sptr; + typedef std::pair<boost::uint16_t, boost::uint16_t> vid_pid_pair_t; /*! * Return the device's serial number @@ -83,6 +84,8 @@ public: * \return a vector of USB device handles that match vid and pid */ static std::vector<usb_device_handle::sptr> get_device_list(boost::uint16_t vid, boost::uint16_t pid); + static std::vector<usb_device_handle::sptr> get_device_list(const std::vector<usb_device_handle::vid_pid_pair_t>& vid_pid_pair_list); + }; //namespace usb diff --git a/host/include/uhd/transport/vrt_if_packet.hpp b/host/include/uhd/transport/vrt_if_packet.hpp index 362531567..1e54607c1 100644 --- a/host/include/uhd/transport/vrt_if_packet.hpp +++ b/host/include/uhd/transport/vrt_if_packet.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // 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 @@ -52,9 +52,18 @@ namespace vrt{ //packet type enum packet_type_t { + // VRT language: PACKET_TYPE_DATA = 0x0, PACKET_TYPE_IF_EXT = 0x1, - PACKET_TYPE_CONTEXT = 0x2 //extension context: has_sid = true + PACKET_TYPE_CONTEXT = 0x2, //extension context: has_sid = true + + // CVITA language: + //PACKET_TYPE_DATA = 0x0, // Data + PACKET_TYPE_FC = 0x1, // Flow control + PACKET_TYPE_ACK = 0x1, // Flow control (ack) + PACKET_TYPE_CMD = 0x2, // Command + PACKET_TYPE_RESP = 0x3, // Command response + PACKET_TYPE_ERROR = 0x3 // Command response: Error (the EOB bit is raised in this case) } packet_type; //size fields @@ -65,18 +74,46 @@ namespace vrt{ //header fields size_t packet_count; + //! Asserted for start- or end-of-burst bool sob, eob; + //! This is asserted for command responses that are errors (CHDR only) + bool error; //optional fields + //! Stream ID (SID). See uhd::sid_t bool has_sid; boost::uint32_t sid; + //! Class ID. bool has_cid; boost::uint64_t cid; + //! Integer timestamp bool has_tsi; boost::uint32_t tsi; + //! Fractional timestamp bool has_tsf; boost::uint64_t tsf; + //! Trailer bool has_tlr; boost::uint32_t tlr; }; /*! * Pack a vrt header from metadata (big endian format). + * + * \section vrt_pack_contract Packing contract + * + * \subsection Requirements: + * - packet_buff points to a valid address space with enough space to write + * the entire buffer, regardless of its length. At the very least, it must + * be able to hold an entire header. + * - `if_packet_info` has the following members set to correct values: + * - `has_*` members all set accordingly + * - For every true `has_*` member, the corresponding variable holds a valid + * value (e.g. if `has_sid` is true, `sid` contains a valid SID) + * - `num_payload_bytes` and `num_payload_words32` are both set to the correct values + * + * \subsection Result: + * - `packet_buff` now points to a valid header that can be sent over the transport + * without further modification + * - The following members on `if_packet_info` are set: + * - `num_header_words32` + * - `num_packet_words32` + * * \param packet_buff memory to write the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -87,6 +124,34 @@ namespace vrt{ /*! * Unpack a vrt header to metadata (big endian format). + * + * \section vrt_unpack_contract Unpacking contract + * + * \subsection Requirements + * - `packet_buff` points to a readable address space with a + * CHDR packet, starting at the header. `packet_buff[0]` *must* always + * point to a valid first word of the header. This implies that num_packet_words32 + * must be at least 1. + * - `if_packet_info` has the following members set to correct values: + * - `num_packet_words32`. This means all values `packet_buff[0]` + * through `packet_buff[if_packet_info.num_packet_words32-1]` are + * readable words from this packet. + * - `link_type` + * + * \subsection Result + * - `if_packet_info` now has the following values set to correct values: + * - `packet_type` + * - `num_payload_bytes` + * - `num_payload_words32` + * - `num_header_words32` + * - `has_*` + * - `sob`, `eob`, `error`, `cid`, `sid` (if applicable) + * - `tsf`, `tsi` (if applicable) + * + * \subsection Exceptions + * - If the header is invalid, but the requirements are still met, + * will throw a uhd::value_error. + * * \param packet_buff memory to read the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -97,6 +162,9 @@ namespace vrt{ /*! * Pack a vrt header from metadata (little endian format). + * + * See \ref vrt_pack_contract. + * * \param packet_buff memory to write the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -107,6 +175,9 @@ namespace vrt{ /*! * Unpack a vrt header to metadata (little endian format). + * + * See \ref vrt_unpack_contract. + * * \param packet_buff memory to read the packed vrt header * \param if_packet_info the if packet info (read/write) */ @@ -124,6 +195,7 @@ namespace vrt{ num_packet_words32(0), packet_count(0), sob(false), eob(false), + error(false), has_sid(false), sid(0), has_cid(false), cid(0), has_tsi(false), tsi(0), diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 9ff3042aa..de1b17e1a 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2015 Ettus Research LLC // // 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 diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index 8bb1de381..3f34782e2 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -17,6 +17,7 @@ UHD_INSTALL(FILES + byte_vector.hpp clock_config.hpp device_addr.hpp dict.ipp @@ -31,11 +32,27 @@ UHD_INSTALL(FILES ref_vector.hpp sensors.hpp serial.hpp + sid.hpp stream_cmd.hpp time_spec.hpp tune_request.hpp tune_result.hpp wb_iface.hpp + filters.hpp DESTINATION ${INCLUDE_DIR}/uhd/types COMPONENT headers ) + +IF(ENABLE_C_API) + UHD_INSTALL(FILES + metadata.h + ranges.h + sensors.h + string_vector.h + tune_request.h + tune_result.h + usrp_info.h + DESTINATION ${INCLUDE_DIR}/uhd/types + COMPONENT headers + ) +ENDIF(ENABLE_C_API) diff --git a/host/include/uhd/types/byte_vector.hpp b/host/include/uhd/types/byte_vector.hpp new file mode 100644 index 000000000..b7637fb5d --- /dev/null +++ b/host/include/uhd/types/byte_vector.hpp @@ -0,0 +1,48 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP +#define INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP + +#include <algorithm> +#include <string> +#include <vector> + +#include <boost/assign.hpp> +#include <boost/cstdint.hpp> + +#include <uhd/config.hpp> + +namespace uhd{ + + //! Byte vector used for I2C data passing and EEPROM parsing. + typedef std::vector<boost::uint8_t> byte_vector_t; + + template<typename RangeSrc, typename RangeDst> UHD_INLINE + void byte_copy(const RangeSrc &src, RangeDst &dst){ + std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); + } + + //! Create a string from a byte vector, terminate when invalid ASCII encountered + UHD_API std::string bytes_to_string(const byte_vector_t &bytes); + + //! Create a byte vector from a string, end at null terminator or max length + UHD_API byte_vector_t string_to_bytes(const std::string &str, size_t max_length); + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_BYTE_VECTOR_HPP */ diff --git a/host/include/uhd/types/dict.hpp b/host/include/uhd/types/dict.hpp index 97fa8f09c..90559ff5f 100644 --- a/host/include/uhd/types/dict.hpp +++ b/host/include/uhd/types/dict.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 Ettus Research LLC // // 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 @@ -117,6 +117,23 @@ namespace uhd{ */ Val pop(const Key &key); + /*! Update this dictionary with values from another. + * + * Basically, this copies all the key/value pairs from \p new_dict + * into this dict. When the key is already present in the current + * dict, it either overwrites the current value (if \p fail_on_conflict + * is false) or it throws (if \p fail_on_conflict is true *and* the + * values differ). + * + * With the exception of \p fail_on_conflict, this behaves analogously + * to Python's dict.update() method. + * + * \param new_dict The arguments to copy. + * \param fail_on_conflict If true, throws. + * \throws uhd::value_error + */ + void update(const dict<Key, Val> &new_dict, bool fail_on_conflict=true); + private: typedef std::pair<Key, Val> pair_t; std::list<pair_t> _map; //private container diff --git a/host/include/uhd/types/dict.ipp b/host/include/uhd/types/dict.ipp index 5e9cf97ad..5fd4b536e 100644 --- a/host/include/uhd/types/dict.ipp +++ b/host/include/uhd/types/dict.ipp @@ -135,6 +135,20 @@ namespace uhd{ throw key_not_found<Key, Val>(key); } + template <typename Key, typename Val> + void dict<Key, Val>::update(const dict<Key, Val> &new_dict, bool fail_on_conflict) + { + BOOST_FOREACH(const Key &key, new_dict.keys()) { + if (fail_on_conflict and has_key(key) and get(key) != new_dict[key]) { + throw uhd::value_error(str( + boost::format("Option merge conflict: %s:%s != %s:%s") + % key % get(key) % key % new_dict[key] + )); + } + set(key, new_dict[key]); + } + } + } //namespace uhd #endif /* INCLUDED_UHD_TYPES_DICT_IPP */ diff --git a/host/include/uhd/types/direction.hpp b/host/include/uhd/types/direction.hpp index 0f257de44..59ee9b55f 100644 --- a/host/include/uhd/types/direction.hpp +++ b/host/include/uhd/types/direction.hpp @@ -1,5 +1,5 @@ // -// Copyright 2015 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // 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 diff --git a/host/include/uhd/types/filters.hpp b/host/include/uhd/types/filters.hpp new file mode 100644 index 000000000..976ae233d --- /dev/null +++ b/host/include/uhd/types/filters.hpp @@ -0,0 +1,286 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_FILTERS_HPP +#define INCLUDED_UHD_TYPES_FILTERS_HPP + +#include <uhd/config.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/scoped_array.hpp> +#include <string> +#include <vector> +#include <iostream> +#include <ostream> +#include <sstream> + +namespace uhd{ + + class UHD_API filter_info_base + { + public: + typedef boost::shared_ptr<filter_info_base> sptr; + enum filter_type + { + ANALOG_LOW_PASS, + ANALOG_BAND_PASS, + DIGITAL_I16, + DIGITAL_FIR_I16 + }; + + filter_info_base( + filter_type type, + bool bypass, + size_t position_index + ): + _type(type), _bypass(bypass), + _position_index(position_index) + { + //NOP + } + + inline virtual bool is_bypassed() + { + return _bypass; + } + + inline filter_type get_type() + { + return _type; + } + + virtual ~filter_info_base() + { + //NOP + } + + virtual std::string to_pp_string(); + + protected: + filter_type _type; + bool _bypass; + size_t _position_index; + + }; + + UHD_API std::ostream& operator<<(std::ostream& os, filter_info_base& f); + + class UHD_API analog_filter_base : public filter_info_base + { + std::string _analog_type; + public: + typedef boost::shared_ptr<analog_filter_base> sptr; + analog_filter_base( + filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type + ): + filter_info_base(type, bypass, position_index), + _analog_type(analog_type) + { + //NOP + } + + inline const std::string& get_analog_type() + { + return _analog_type; + } + + virtual std::string to_pp_string(); + }; + + class UHD_API analog_filter_lp : public analog_filter_base + { + double _cutoff; + double _rolloff; + + public: + typedef boost::shared_ptr<analog_filter_lp> sptr; + analog_filter_lp( + filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type, + double cutoff, + double rolloff + ): + analog_filter_base(type, bypass, position_index, analog_type), + _cutoff(cutoff), + _rolloff(rolloff) + { + //NOP + } + + inline double get_cutoff() + { + return _cutoff; + } + + inline double get_rolloff() + { + return _cutoff; + } + + inline void set_cutoff(const double cutoff) + { + _cutoff = cutoff; + } + + virtual std::string to_pp_string(); + }; + + template<typename tap_t> + class UHD_API digital_filter_base : public filter_info_base + { + protected: + double _rate; + boost::uint32_t _interpolation; + boost::uint32_t _decimation; + tap_t _tap_full_scale; + boost::uint32_t _max_num_taps; + std::vector<tap_t> _taps; + + public: + typedef boost::shared_ptr<digital_filter_base> sptr; + digital_filter_base( + filter_type type, + bool bypass, + size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + double tap_full_scale, + size_t max_num_taps, + const std::vector<tap_t>& taps + ): + filter_info_base(type, bypass, position_index), + _rate(rate), + _interpolation(interpolation), + _decimation(decimation), + _tap_full_scale(tap_full_scale), + _max_num_taps(max_num_taps), + _taps(taps) + { + //NOP + } + + inline double get_output_rate() + { + return (_bypass ? _rate : (_rate / _decimation * _interpolation)); + } + + inline double get_input_rate() + { + return _rate; + } + + inline double get_interpolation() + { + return _interpolation; + } + + inline double get_decimation() + { + return _decimation; + } + + inline double get_tap_full_scale() + { + return _tap_full_scale; + } + + inline std::vector<tap_t>& get_taps() + { + return _taps; + } + + virtual std::string to_pp_string() + { + std::ostringstream os; + os<<filter_info_base::to_pp_string()<< + "\t[digital_filter_base]"<<std::endl<< + "\tinput rate: "<<_rate<<std::endl<< + "\tinterpolation: "<<_interpolation<<std::endl<< + "\tdecimation: "<<_decimation<<std::endl<< + "\tfull-scale: "<<_tap_full_scale<<std::endl<< + "\tmax num taps: "<<_max_num_taps<<std::endl<< + "\ttaps: "<<std::endl; + + os<<"\t\t"; + for(size_t i = 0; i < _taps.size(); i++) + { + os<<"(tap "<<i<<": "<<_taps[i]<<")"; + if( ((i%10) == 0) && (i != 0)) + { + os<<std::endl<<"\t\t"; + } + } + os<<std::endl; + return std::string(os.str()); + } + + }; + + template<typename tap_t> + class UHD_API digital_filter_fir : public digital_filter_base<tap_t> + { + public: + typedef boost::shared_ptr<digital_filter_fir<tap_t> > sptr; + + digital_filter_fir( + filter_info_base::filter_type type, + bool bypass, size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + size_t tap_bit_width, + size_t max_num_taps, + const std::vector<tap_t>& taps + ): + digital_filter_base<tap_t>(type, bypass, position_index, rate, interpolation, decimation, tap_bit_width, max_num_taps, taps) + { + //NOP + } + + void set_taps(const std::vector<tap_t>& taps) + { + std::size_t num_taps = taps.size(); + if(num_taps < this->_max_num_taps){ + UHD_MSG(warning) << "digital_filter_fir::set_taps not enough coefficients. Appending zeros"; + std::vector<tap_t> coeffs; + for (size_t i = 0; i < this->_max_num_taps; i++) + { + if(i < num_taps) + { + coeffs.push_back(taps[i]); + } else { + coeffs.push_back(0); + } + } + this->_taps = coeffs; + } else { + this->_taps = taps; + } + } + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_FILTERS_HPP */ diff --git a/host/include/uhd/types/metadata.h b/host/include/uhd/types/metadata.h new file mode 100644 index 000000000..0cdbc6a72 --- /dev/null +++ b/host/include/uhd/types/metadata.h @@ -0,0 +1,364 @@ +/* + * Copyright 2015 Ettus Research + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_UHD_TYPES_METADATA_H +#define INCLUDED_UHD_TYPES_METADATA_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> + +#ifdef __cplusplus +#include <uhd/types/metadata.hpp> +#include <string> + +struct uhd_rx_metadata_t { + uhd::rx_metadata_t rx_metadata_cpp; + std::string last_error; +}; + +struct uhd_tx_metadata_t { + uhd::tx_metadata_t tx_metadata_cpp; + std::string last_error; +}; + +struct uhd_async_metadata_t { + uhd::async_metadata_t async_metadata_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_rx_metadata_t; +struct uhd_tx_metadata_t; +struct uhd_async_metadata_t; +#endif + +//! RX metadata interface for describing sent IF data. +/*! + * See uhd::rx_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_rx_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_rx_metadata_t* uhd_rx_metadata_handle; + +//! TX metadata interface for describing received IF data. +/*! + * See uhd::tx_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_tx_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_tx_metadata_t* uhd_tx_metadata_handle; + +//! Interface for describing transmit-related events. +/*! + * See uhd::async_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_async_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_async_metadata_t* uhd_async_metadata_handle; + +//! Error condition on a receive call +/*! + * See uhd::rx_metadata_t::error_code_t for more details. + */ +typedef enum { + //! No error code associated with this metadata + UHD_RX_METADATA_ERROR_CODE_NONE = 0x0, + //! No packet received, implementation timed out + UHD_RX_METADATA_ERROR_CODE_TIMEOUT = 0x1, + //! A stream command was issued in the past + UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND = 0x2, + //! Expected another stream command + UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN = 0x4, + //! Overflow or sequence error + UHD_RX_METADATA_ERROR_CODE_OVERFLOW = 0x8, + //! Multi-channel alignment failed + UHD_RX_METADATA_ERROR_CODE_ALIGNMENT = 0xC, + //! The packet could not be parsed + UHD_RX_METADATA_ERROR_CODE_BAD_PACKET = 0xF +} uhd_rx_metadata_error_code_t; + + +//! Create a new RX metadata handle +UHD_API uhd_error uhd_rx_metadata_make( + uhd_rx_metadata_handle* handle +); + +//! Free an RX metadata handle +/*! + * Using a handle after freeing it here will result in a segmentation fault. + */ +UHD_API uhd_error uhd_rx_metadata_free( + uhd_rx_metadata_handle* handle +); + +//! Has time specification? +UHD_API uhd_error uhd_rx_metadata_has_time_spec( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Time of first sample +UHD_API uhd_error uhd_rx_metadata_time_spec( + uhd_rx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +); + +//! Fragmentation flag +UHD_API uhd_error uhd_rx_metadata_more_fragments( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Fragmentation offset +UHD_API uhd_error uhd_rx_metadata_fragment_offset( + uhd_rx_metadata_handle h, + size_t *fragment_offset_out +); + +//! Start of burst? +UHD_API uhd_error uhd_rx_metadata_start_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! End of burst? +UHD_API uhd_error uhd_rx_metadata_end_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Result out of sequence? +UHD_API uhd_error uhd_rx_metadata_out_of_sequence( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Return a pretty-print representation of this metadata. +/*! + * NOTE: This function will overwrite any string in the given buffer + * before inserting the pp_string. + * + * \param h metadata handle + * \param pp_string_out string buffer for pp_string + * \param buffer length + */ +UHD_API uhd_error uhd_rx_metadata_to_pp_string( + uhd_rx_metadata_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error state of the RX metadata object. +UHD_API uhd_error uhd_rx_metadata_error_code( + uhd_rx_metadata_handle h, + uhd_rx_metadata_error_code_t *error_code_out +); + +//! Get a string representation of the last error state of the RX metadata object. +/*! + * NOTES: + * <ul> + * <li>This is different from the error that can be retrieved with + * uhd_rx_metadata_last_error. See uhd::rx_metadata_t::strerror() for details.</li> + * <li>This function will overwrite any string in the given buffer before + * inserting the error string.</li> + * </ul> + * + * \param h metadata handle + * \param strerror_out string buffer for strerror + * \param buffer length + */ +UHD_API uhd_error uhd_rx_metadata_strerror( + uhd_rx_metadata_handle h, + char* strerror_out, + size_t strbuffer_len +); + +//! Get the last error logged by the RX metadata object. +/*! + * NOTES: + * <ul> + * <li>This is different from the error that can be retrieved with + * uhd_rx_metadata_strerror(). See <uhd/error.h> for details.</li> + * <li>This function will overwrite any string in the given buffer before + * inserting the error string.</li> + * </ul> + * + * \param h metadata handle + * \param error_out string buffer for error + * \param buffer length + */ +UHD_API uhd_error uhd_rx_metadata_last_error( + uhd_rx_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Create a new TX metadata handle +UHD_API uhd_error uhd_tx_metadata_make( + uhd_tx_metadata_handle* handle, + bool has_time_spec, + time_t full_secs, + double frac_secs, + bool start_of_burst, + bool end_of_burst +); + + +//! Free an TX metadata handle +/*! + * Using a handle after freeing it here will result in a segmentation fault. + */ +UHD_API uhd_error uhd_tx_metadata_free( + uhd_tx_metadata_handle* handle +); + +//! Has time specification? +UHD_API uhd_error uhd_tx_metadata_has_time_spec( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! Get time specification +UHD_API uhd_error uhd_tx_metadata_time_spec( + uhd_tx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +); + +//! Start of burst? +UHD_API uhd_error uhd_tx_metadata_start_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! End of burst? +UHD_API uhd_error uhd_tx_metadata_end_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! Get the last error logged by the TX metadata object. +/*! + * NOTE: This function will overwrite any string in the given buffer before + * inserting the error string. + * + * \param h metadata handle + * \param error_out string buffer for error + * \param buffer length + */ +UHD_API uhd_error uhd_tx_metadata_last_error( + uhd_tx_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +//! The type of event for a receive async message call. +/*! + * See uhd::async_metadata_t::event_code_t for more details. + */ +typedef enum { + //! A burst was successfully transmitted. + UHD_ASYNC_METADATA_EVENT_CODE_BURST_ACK = 0x1, + //! An internal send buffer has emptied. + UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW = 0x2, + //! Packet loss error between host and device. + UHD_ASYNC_METADATA_EVENT_CODE_SEQ_ERROR = 0x4, + //! Packet had time that was late. + UHD_ASYNC_METADATA_EVENT_CODE_TIME_ERROR = 0x8, + //! Underflow occurred inside a packet. + UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW_IN_PACKET = 0x10, + //! Packet loss within a burst. + UHD_ASYNC_METADATA_EVENT_CODE_SEQ_ERROR_IN_BURST = 0x20, + //! Some kind of custom user payload. + UHD_ASYNC_METADATA_EVENT_CODE_USER_PAYLOAD = 0x40 +} uhd_async_metadata_event_code_t; + +//! Create a new async metadata handle +UHD_API uhd_error uhd_async_metadata_make( + uhd_async_metadata_handle* handle +); + +//! Free an async metadata handle +/*! + * Using a handle after freeing it will result in a segmentation fault. + */ +UHD_API uhd_error uhd_async_metadata_free( + uhd_async_metadata_handle* handle +); + +//! Channel number in a MIMO configuration +UHD_API uhd_error uhd_async_metadata_channel( + uhd_async_metadata_handle h, + size_t *channel_out +); + +//! Has time specification? +UHD_API uhd_error uhd_async_metadata_has_time_spec( + uhd_async_metadata_handle h, + bool *result_out +); + +//! Get time specification +UHD_API uhd_error uhd_async_metadata_time_spec( + uhd_async_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +); + +//! Get last event code +UHD_API uhd_error uhd_async_metadata_event_code( + uhd_async_metadata_handle h, + uhd_async_metadata_event_code_t *event_code_out +); + +//! Get payload from custom FPGA fabric +UHD_API uhd_error uhd_async_metadata_user_payload( + uhd_async_metadata_handle h, + uint32_t user_payload_out[4] +); + +//! Get the last error logged by the async metadata object. +/*! + * NOTE: This function will overwrite any string in the given buffer before + * inserting the error string. + * + * \param h metadata handle + * \param error_out string buffer for error + * \param buffer length + */ +UHD_API uhd_error uhd_async_metadata_last_error( + uhd_async_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_TYPES_METADATA_H */ diff --git a/host/include/uhd/types/ranges.h b/host/include/uhd/types/ranges.h new file mode 100644 index 000000000..ca80c9141 --- /dev/null +++ b/host/include/uhd/types/ranges.h @@ -0,0 +1,154 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_RANGES_H +#define INCLUDED_UHD_TYPES_RANGES_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#include <stdbool.h> +#include <stdlib.h> + +//! Range of floating-point values +typedef struct { + //! First value + double start; + //! Last value + double stop; + //! Granularity + double step; +} uhd_range_t; + +#ifdef __cplusplus +#include <uhd/types/ranges.hpp> +#include <string> + +struct uhd_meta_range_t { + uhd::meta_range_t meta_range_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_meta_range_t; +#endif + +//! C-level interface for dealing with a list of ranges +/*! + * See uhd::meta_range_t for more details. + */ +typedef struct uhd_meta_range_t* uhd_meta_range_handle; + +//! Get a string representation of the given range +UHD_API uhd_error uhd_range_to_pp_string( + const uhd_range_t *range, + char* pp_string_out, + size_t strbuffer_len +); + +//! Create a meta range handle +/*! + * NOTE: Using a uhd_meta_range_handle before passing it into this function will + * result in undefined behavior. + */ +UHD_API uhd_error uhd_meta_range_make( + uhd_meta_range_handle* h +); + +//! Destroy a meta range handle +/*! + * NOTE: Using a uhd_meta_range_handle after passing it into this function will + * result in a segmentation fault. + */ +UHD_API uhd_error uhd_meta_range_free( + uhd_meta_range_handle* h +); + +//! Get the overall start value for the given meta range +UHD_API uhd_error uhd_meta_range_start( + uhd_meta_range_handle h, + double *start_out +); + +//! Get the overall stop value for the given meta range +UHD_API uhd_error uhd_meta_range_stop( + uhd_meta_range_handle h, + double *stop_out +); + +//! Get the overall step value for the given meta range +UHD_API uhd_error uhd_meta_range_step( + uhd_meta_range_handle h, + double *step_out +); + +//! Clip the given value to a possible value in the given range +UHD_API uhd_error uhd_meta_range_clip( + uhd_meta_range_handle h, + double value, + bool clip_step, + double *result_out +); + +//! Get the number of ranges in the given meta range +UHD_API uhd_error uhd_meta_range_size( + uhd_meta_range_handle h, + size_t *size_out +); + +//! Add a range to the given meta range +UHD_API uhd_error uhd_meta_range_push_back( + uhd_meta_range_handle h, + const uhd_range_t *range +); + +//! Get the range at the given index +UHD_API uhd_error uhd_meta_range_at( + uhd_meta_range_handle h, + size_t num, + uhd_range_t *range_out +); + +//! Get a string representation of the given meta range +UHD_API uhd_error uhd_meta_range_to_pp_string( + uhd_meta_range_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error recorded by the underlying meta range +UHD_API uhd_error uhd_meta_range_last_error( + uhd_meta_range_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} + +UHD_API uhd::range_t uhd_range_c_to_cpp( + const uhd_range_t *range_c +); + +UHD_API void uhd_range_cpp_to_c( + const uhd::range_t &range_cpp, + uhd_range_t *range_c +); +#endif + +#endif /* INCLUDED_UHD_TYPES_RANGES_H */ diff --git a/host/include/uhd/types/sensors.h b/host/include/uhd/types/sensors.h new file mode 100644 index 000000000..c0037c9f6 --- /dev/null +++ b/host/include/uhd/types/sensors.h @@ -0,0 +1,231 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_SENSORS_H +#define INCLUDED_UHD_TYPES_SENSORS_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#ifdef __cplusplus +#include <uhd/types/sensors.hpp> +#include <string> + +struct uhd_sensor_value_t { + // No default constructor, so we need a pointer + uhd::sensor_value_t* sensor_value_cpp; + std::string last_error; +}; +extern "C" { +#else +struct uhd_sensor_value_t; +#endif + +//! C-level interface for a UHD sensor +/*! + * See uhd::sensor_value_t for more details. + * + * NOTE: Using a handle before calling a make function will result in undefined behavior. + */ +typedef struct uhd_sensor_value_t* uhd_sensor_value_handle; + +//! Sensor value types +typedef enum { + UHD_SENSOR_VALUE_BOOLEAN = 98, + UHD_SENSOR_VALUE_INTEGER = 105, + UHD_SENSOR_VALUE_REALNUM = 114, + UHD_SENSOR_VALUE_STRING = 115 +} uhd_sensor_value_data_type_t; + +//! Make a UHD sensor from a boolean. +/*! + * \param h the sensor handle in which to place sensor + * \param name sensor name + * \param value sensor value + * \param utrue string representing "true" + * \param ufalse string representing "false" + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_bool( + uhd_sensor_value_handle* h, + const char* name, + bool value, + const char* utrue, + const char* ufalse +); + +//! Make a UHD sensor from an integer. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \param formatter printf-style format string for value string + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_int( + uhd_sensor_value_handle* h, + const char* name, + int value, + const char* unit, + const char* formatter +); + +//! Make a UHD sensor from a real number. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \param formatter printf-style format string for value string + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_realnum( + uhd_sensor_value_handle* h, + const char* name, + double value, + const char* unit, + const char* formatter +); + +//! Make a UHD sensor from a string. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_string( + uhd_sensor_value_handle* h, + const char* name, + const char* value, + const char* unit +); + +//! Free the given sensor handle. +/*! + * Attempting to use the handle after calling this handle will + * result in a segmentation fault. + */ +UHD_API uhd_error uhd_sensor_value_free( + uhd_sensor_value_handle* h +); + +//! Get the sensor's value as a boolean. +UHD_API uhd_error uhd_sensor_value_to_bool( + uhd_sensor_value_handle h, + bool *value_out +); + +//! Get the sensor's value as an integer. +UHD_API uhd_error uhd_sensor_value_to_int( + uhd_sensor_value_handle h, + int *value_out +); + +//! Get the sensor's value as a real number. +UHD_API uhd_error uhd_sensor_value_to_realnum( + uhd_sensor_value_handle h, + double *value_out +); + +//! Get the sensor's name. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor name. + * + * \param h sensor handle + * \param name_out string buffer in which to place name + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_name( + uhd_sensor_value_handle h, + char* name_out, + size_t strbuffer_len +); + +//! Get the sensor's value. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor value. + * + * \param h sensor handle + * \param value_out string buffer in which to place value + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_value( + uhd_sensor_value_handle h, + char* value_out, + size_t strbuffer_len +); + +//! Get the sensor's unit. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor unit. + * + * \param h sensor handle + * \param unit_out string buffer in which to place unit + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_unit( + uhd_sensor_value_handle h, + char* unit_out, + size_t strbuffer_len +); + +UHD_API uhd_error uhd_sensor_value_data_type( + uhd_sensor_value_handle h, + uhd_sensor_value_data_type_t *data_type_out +); + +//! Get a pretty-print representation of the given sensor. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the string. + * + * \param h sensor handle + * \param pp_string_out string buffer in which to place pp_string + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_to_pp_string( + uhd_sensor_value_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error logged by the sensor handle. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the error string. + * + * \param h sensor handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_last_error( + uhd_sensor_value_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_TYPES_SENSORS_H */ diff --git a/host/include/uhd/types/sid.hpp b/host/include/uhd/types/sid.hpp new file mode 100644 index 000000000..95034c7a5 --- /dev/null +++ b/host/include/uhd/types/sid.hpp @@ -0,0 +1,238 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_SID_HPP +#define INCLUDED_UHD_TYPES_SID_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <iostream> + +namespace uhd { + /*! + * \brief Represents a stream ID (SID). + * + * A stream ID (SID) is an identifier for data. + * It is a 32-Bit value which consistst of 16 Bits + * for the source address and 16 Bits for the destination + * address. + * Every address is split into two parts: The _address_, which + * identifies the device used, and the _endpoint_, which identifies + * a specific object inside the given device (e.g., a block). + * *Note:* In the case where there are several crossbars on a single + * device, each crossbar gets its own address. + * Both address and endpoint are 8 bits in length. If a 16-bit address + * is required, we use the combination of the 8-bit address and the 8-bit + * endpoint. + * + * \section sid_str_repr String Representation + * + * The string representation of a SID is of the form + * + * 2.3>0.6 + * + * The '>' symbol shows the direction, so in this case, + * data is flowing from address 2.3 to 0.6. + * + * As a convention, ':' is used instead of '.' when giving the + * SID in hexadecimal numbers, and two characters are used for each + * address part. As an example, the following two SIDs are identical: + * + * 2.3>0.16 (decimal) + * 02:03>00:10 (hexadecimal) + * + * The format is: + * SRC_ADDRESS.SRC_ENDPOINT>DST_ADDRESS.DST_ENDPOINT + * + * + * \section sid_block_ports Block Ports + * + * In the special case where a block on a crossbar is addressed, the + * endpoint is further split up into two parts of four bits each: The + * first four bits specify the port number on the crossbar, whereas the + * lower four bits represent the *block port*. As an example, consider + * the following SID, given in hexadecimal: + * + * 00:10>02:A1 + * + * In this example, assume data is flowing from the host computer to an + * X300. The crossbar address is 02. The endpoint is A1, which means we + * are accessing a block on crossbar port A (the tenth port), and are addressing + * block port 1. + * + */ + class UHD_API sid_t + { + public: + //! Create an unset SID + sid_t(); + //! Create a sid_t object from a 32-Bit SID value + sid_t(boost::uint32_t sid); + //! Create a sid_t object from its four components + sid_t(boost::uint8_t src_addr, boost::uint8_t src_ep, boost::uint8_t dst_addr, boost::uint8_t dst_ep); + //! Convert a string representation of a SID into its numerical representation + sid_t(const std::string &); + + //! Return a decimal string representation of the SID. + std::string to_pp_string() const; + //! Return a hexadecimal string representation of the SID. + std::string to_pp_string_hex() const; + + //! Returns true if this actually holds a valid SID + bool is_set() const { return _set; }; + + // Getters + // + //! Alias for get_sid() + UHD_INLINE boost::uint32_t get() const { return get_sid(); }; + //! Returns a 32-Bit representation of the SID if set, or zero otherwise. + UHD_INLINE boost::uint32_t get_sid() const { return _set ? _sid : 0; }; + //! Return the 16-bit source address of this SID + UHD_INLINE boost::uint32_t get_src() const { + return (_sid >> 16) & 0xFFFF; + } + //! Return the 16-bit destination address of this SID + UHD_INLINE boost::uint32_t get_dst() const { + return _sid & 0xFFFF; + } + //! Return 8-bit address of the source + UHD_INLINE boost::uint32_t get_src_addr() const { + return (get_src() >> 8) & 0xFF; + } + //! Return endpoint of the source + UHD_INLINE boost::uint32_t get_src_endpoint() const { + return get_src() & 0xFF; + } + //! Return crossbar port of the source + UHD_INLINE boost::uint32_t get_src_xbarport() const { + return (get_src_endpoint() >> 4) & 0xF; + } + //! Return block port of the source + UHD_INLINE boost::uint32_t get_src_blockport() const { + return (get_src_endpoint()) & 0xF; + } + //! Return 8-bit address of the destination + UHD_INLINE boost::uint32_t get_dst_addr() const { + return (get_dst() >> 8) & 0xFF; + } + //! Return endpoint of the destination + UHD_INLINE boost::uint32_t get_dst_endpoint() const { + return get_dst() & 0xFF; + } + //! Return crossbar port of the source + UHD_INLINE boost::uint32_t get_dst_xbarport() const { + return (get_dst_endpoint() >> 4) & 0xF; + } + //! Return block port of the source + UHD_INLINE boost::uint32_t get_dst_blockport() const { + return (get_dst_endpoint()) & 0xF; + } + + // Setters + + //! Alias for set_sid() + void set(boost::uint32_t new_sid) { set_sid(new_sid); }; + //! Convert a string representation of a SID into a numerical one + // Throws uhd::value_error if the string is not a valid SID + // representation. + void set_from_str(const std::string &); + void set_sid(boost::uint32_t new_sid); + //! Set the source address of this SID + // (the first 16 Bits) + void set_src(boost::uint32_t new_addr); + //! Set the destination address of this SID + // (the last 16 Bits) + void set_dst(boost::uint32_t new_addr); + void set_src_addr(boost::uint32_t new_addr); + void set_src_endpoint(boost::uint32_t new_addr); + void set_dst_addr(boost::uint32_t new_addr); + void set_dst_endpoint(boost::uint32_t new_addr); + void set_dst_xbarport(boost::uint32_t new_xbarport); + void set_dst_blockport(boost::uint32_t new_blockport); + + // Manipulators + + //! Swaps dst and src address and returns the new SID. + sid_t reversed(); + + //! Swaps dst and src in-place. + void reverse(); + + // Overloaded operators + + sid_t operator = (boost::uint32_t new_sid) { + set_sid(new_sid); + return *this; + } + + sid_t operator = (sid_t &sid) { + set_sid(sid.get_sid()); + return *this; + } + + sid_t operator = (const std::string &sid_str) { + set_from_str(sid_str); + return *this; + } + + bool operator == (const sid_t &sid) const { + return (not _set and not sid.is_set()) or (_sid == sid.get_sid()); + } + + bool operator == (boost::uint32_t sid) const { + return _set and _sid == sid; + } + + bool operator == (const std::string &sid_str) const { + sid_t rhs(sid_str); + return *this == rhs; + } + + // overloaded type casts are tricky, but for now we'll need them + // for backward compatibility. consider them deprecated. + + //! If the SID is not set, always returns zero. + // Use is_set() to check if the return value is valid. + operator boost::uint32_t() const { + return get(); + } + + operator bool() const { + return _set; + } + + private: + boost::uint32_t _sid; + bool _set; + }; + + //! Stream output operator. Honors std::ios::hex. + UHD_INLINE std::ostream& operator<< (std::ostream& out, const sid_t &sid) { + std::ios_base::fmtflags ff = out.flags(); + if (ff & std::ios::hex) { + out << sid.to_pp_string_hex(); + } else { + out << sid.to_pp_string(); + } + return out; + } + +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_SID_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/types/stream_cmd.hpp b/host/include/uhd/types/stream_cmd.hpp index 3c34c9656..c24296bd6 100644 --- a/host/include/uhd/types/stream_cmd.hpp +++ b/host/include/uhd/types/stream_cmd.hpp @@ -1,19 +1,19 @@ -// -// Copyright 2010-2012 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// +/* + * Copyright 2010-2012,2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ #ifndef INCLUDED_UHD_TYPES_STREAM_CMD_HPP #define INCLUDED_UHD_TYPES_STREAM_CMD_HPP @@ -59,6 +59,6 @@ namespace uhd{ stream_cmd_t(const stream_mode_t &stream_mode); }; -} //namespace uhd +} /* namespace uhd */ #endif /* INCLUDED_UHD_TYPES_STREAM_CMD_HPP */ diff --git a/host/include/uhd/types/string_vector.h b/host/include/uhd/types/string_vector.h new file mode 100644 index 000000000..685b05f3f --- /dev/null +++ b/host/include/uhd/types/string_vector.h @@ -0,0 +1,86 @@ +/* + * Copyright 2015 Ettus Research + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_UHD_TYPES_STRING_VECTOR_H +#define INCLUDED_UHD_TYPES_STRING_VECTOR_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#include <stdlib.h> + +#ifdef __cplusplus +#include <string> +#include <vector> + +struct uhd_string_vector_t { + std::vector<std::string> string_vector_cpp; + std::string last_error; +}; + +extern "C" { +#else +//! C-level read-only interface for interacting with a string vector +struct uhd_string_vector_t; +#endif + +typedef struct uhd_string_vector_t uhd_string_vector_t; + +typedef uhd_string_vector_t* uhd_string_vector_handle; + +//! Instantiate a string_vector handle. +UHD_API uhd_error uhd_string_vector_make( + uhd_string_vector_handle *h +); + +//! Safely destroy a string_vector handle. +UHD_API uhd_error uhd_string_vector_free( + uhd_string_vector_handle *h +); + +//! Add a string to the list +UHD_API uhd_error uhd_string_vector_push_back( + uhd_string_vector_handle *h, + const char* value +); + +//! Get the string at the given index +UHD_API uhd_error uhd_string_vector_at( + uhd_string_vector_handle h, + size_t index, + char* value_out, + size_t strbuffer_len +); + +//! Get the number of strings in this list +UHD_API uhd_error uhd_string_vector_size( + uhd_string_vector_handle h, + size_t *size_out +); + +//! Get the last error reported by the underlying object +UHD_API uhd_error uhd_string_vector_last_error( + uhd_string_vector_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_TYPES_STRING_VECTOR_H */ diff --git a/host/include/uhd/types/tune_request.h b/host/include/uhd/types/tune_request.h new file mode 100644 index 000000000..046350643 --- /dev/null +++ b/host/include/uhd/types/tune_request.h @@ -0,0 +1,61 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_TUNE_REQUEST_H +#define INCLUDED_UHD_TYPES_TUNE_REQUEST_H + +#include <uhd/config.h> + +#include <stdlib.h> + +//! Policy options for tunable elements in the RF chain. +typedef enum { + //! Do not set this argument, use current setting. + UHD_TUNE_REQUEST_POLICY_NONE = 78, + //! Automatically determine the argument's value. + UHD_TUNE_REQUEST_POLICY_AUTO = 65, + //! Use the argument's value for the setting. + UHD_TUNE_REQUEST_POLICY_MANUAL = 77 +} uhd_tune_request_policy_t; + +//! Instructs implementation how to tune the RF chain +/*! + * See uhd::tune_request_t for more details. + */ +typedef struct { + //! Target frequency for RF chain in Hz + double target_freq; + //! RF frequency policy + uhd_tune_request_policy_t rf_freq_policy; + //! RF frequency in Hz + double rf_freq; + //! DSP frequency policy + uhd_tune_request_policy_t dsp_freq_policy; + //! DSP frequency in Hz + double dsp_freq; + //! Key-value pairs delimited by commas + char* args; +} uhd_tune_request_t; + +#ifdef __cplusplus +#include <uhd/types/tune_request.hpp> + +UHD_API uhd::tune_request_t uhd_tune_request_c_to_cpp(uhd_tune_request_t *tune_request_c); + +#endif + +#endif /* INCLUDED_UHD_TYPES_TUNE_REQUEST_H */ diff --git a/host/include/uhd/types/tune_result.h b/host/include/uhd/types/tune_result.h new file mode 100644 index 000000000..e0d00cd2e --- /dev/null +++ b/host/include/uhd/types/tune_result.h @@ -0,0 +1,60 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_TUNE_RESULT_H +#define INCLUDED_UHD_TYPES_TUNE_RESULT_H + +#include <uhd/config.h> + +#include <stdlib.h> + +//! Stores RF and DSP tuned frequencies. +/*! + * See uhd::tune_result_t for more details. + */ +typedef struct { + //! Target RF frequency, clipped to be within system range + double clipped_rf_freq; + //! Target RF frequency, including RF FE offset + double target_rf_freq; + //! Frequency to which RF LO is actually tuned + double actual_rf_freq; + //! Frequency the CORDIC must adjust the RF + double target_dsp_freq; + //! Frequency to which the CORDIC in the DSP actually tuned + double actual_dsp_freq; +} uhd_tune_result_t; + +#ifdef __cplusplus +extern "C" { +#endif + +//! Create a pretty print representation of this tune result. +UHD_API void uhd_tune_result_to_pp_string(uhd_tune_result_t *tune_result, + char* pp_string_out, size_t strbuffer_len); + +#ifdef __cplusplus +} +#include <uhd/types/tune_result.hpp> + +UHD_API uhd::tune_result_t uhd_tune_result_c_to_cpp(uhd_tune_result_t *tune_result_c); + +UHD_API void uhd_tune_result_cpp_to_c(const uhd::tune_result_t &tune_result_cpp, + uhd_tune_result_t *tune_result_c); +#endif + +#endif /* INCLUDED_UHD_TYPES_TUNE_RESULT_H */ diff --git a/host/include/uhd/types/usrp_info.h b/host/include/uhd/types/usrp_info.h new file mode 100644 index 000000000..c118963da --- /dev/null +++ b/host/include/uhd/types/usrp_info.h @@ -0,0 +1,94 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_UHD_TYPES_USRP_INFO_H +#define INCLUDED_UHD_TYPES_USRP_INFO_H + +#include <uhd/config.h> +#include <uhd/error.h> + +//! USRP RX info +/*! + * This struct is populated by uhd_usrp_get_rx_info(). + */ +typedef struct { + //! Motherboard ID + char* mboard_id; + //! Motherboard name + char* mboard_name; + //! Motherboard serial + char* mboard_serial; + //! RX daughterboard ID + char* rx_id; + //! RX subdev name + char* rx_subdev_name; + //! RX subdev spec + char* rx_subdev_spec; + //! RX daughterboard serial + char* rx_serial; + //! RX daughterboard antenna + char* rx_antenna; +} uhd_usrp_rx_info_t; + +//! USRP TX info +/*! + * This struct is populated by uhd_usrp_get_tx_info(). + */ +typedef struct { + //! Motherboard ID + char* mboard_id; + //! Motherboard name + char* mboard_name; + //! Motherboard serial + char* mboard_serial; + //! TX daughterboard ID + char* tx_id; + //! TX subdev name + char* tx_subdev_name; + //! TX subdev spec + char* tx_subdev_spec; + //! TX daughterboard serial + char* tx_serial; + //! TX daughterboard antenna + char* tx_antenna; +} uhd_usrp_tx_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + +//! Clean up a uhd_usrp_rx_info_t populated by uhd_usrp_get_rx_info(). +/*! + * NOTE: If this function is passed a uhd_usrp_rx_info_t that has not + * been populated by uhd_usrp_get_rx_info(), it will produce a double-free + * error. + */ +UHD_API uhd_error uhd_usrp_rx_info_free(uhd_usrp_rx_info_t *rx_info); + +//! Clean up a uhd_usrp_tx_info_t populated by uhd_usrp_get_tx_info(). +/*! + * NOTE: If this function is passed a uhd_usrp_tx_info_t that has not + * been populated by uhd_usrp_get_tx_info(), it will produce a double-free + * error. + */ +UHD_API uhd_error uhd_usrp_tx_info_free(uhd_usrp_tx_info_t *tx_info); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_TYPES_USRP_INFO_H */ diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index d30a2900a..e974f808d 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -15,7 +15,6 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # - UHD_INSTALL(FILES #### dboard headers ### @@ -36,3 +35,14 @@ UHD_INSTALL(FILES DESTINATION ${INCLUDE_DIR}/uhd/usrp COMPONENT headers ) + +IF(ENABLE_C_API) + UHD_INSTALL(FILES + dboard_eeprom.h + mboard_eeprom.h + subdev_spec.h + usrp.h + DESTINATION ${INCLUDE_DIR}/uhd/usrp + COMPONENT headers + ) +ENDIF(ENABLE_C_API) diff --git a/host/include/uhd/usrp/dboard_eeprom.h b/host/include/uhd/usrp/dboard_eeprom.h new file mode 100644 index 000000000..6980de0ce --- /dev/null +++ b/host/include/uhd/usrp/dboard_eeprom.h @@ -0,0 +1,110 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_DBOARD_EEPROM_H +#define INCLUDED_UHD_USRP_DBOARD_EEPROM_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#ifdef __cplusplus +#include <uhd/usrp/dboard_eeprom.hpp> +#include <string> + +struct uhd_dboard_eeprom_t { + uhd::usrp::dboard_eeprom_t dboard_eeprom_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_dboard_eeprom_t; +#endif + +//! A C-level interface for interacting with a daughterboard EEPROM +/*! + * See uhd::usrp::dboard_eeprom_t for more details. + * + * NOTE: Using a handle before passing it into uhd_dboard_eeprom_make() will + * result in undefined behavior. + */ +typedef struct uhd_dboard_eeprom_t* uhd_dboard_eeprom_handle; + +//! Create handle for a USRP daughterboard EEPROM +UHD_API uhd_error uhd_dboard_eeprom_make( + uhd_dboard_eeprom_handle* h +); + +//! Safely destroy the given handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_dboard_eeprom_free( + uhd_dboard_eeprom_handle* h +); + +//! Get the ID associated with the given daughterboard as a string hex representation +UHD_API uhd_error uhd_dboard_eeprom_get_id( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +); + +//! Set the daughterboard ID using a string hex representation +UHD_API uhd_error uhd_dboard_eeprom_set_id( + uhd_dboard_eeprom_handle h, + const char* id +); + +//! Get the daughterboard's serial +UHD_API uhd_error uhd_dboard_eeprom_get_serial( + uhd_dboard_eeprom_handle h, + char* serial_out, + size_t strbuffer_len +); + +//! Set the daughterboard's serial +UHD_API uhd_error uhd_dboard_eeprom_set_serial( + uhd_dboard_eeprom_handle h, + const char* serial +); + +//! Get the daughterboard's revision (not always present) +UHD_API uhd_error uhd_dboard_eeprom_get_revision( + uhd_dboard_eeprom_handle h, + int* revision_out +); + +//! Set the daughterboard's revision +UHD_API uhd_error uhd_dboard_eeprom_set_revision( + uhd_dboard_eeprom_handle h, + int revision +); + +//! Get the last error reported by the handle +UHD_API uhd_error uhd_dboard_eeprom_last_error( + uhd_dboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_USRP_DBOARD_EEPROM_H */ diff --git a/host/include/uhd/usrp/mboard_eeprom.h b/host/include/uhd/usrp/mboard_eeprom.h new file mode 100644 index 000000000..b4474a26d --- /dev/null +++ b/host/include/uhd/usrp/mboard_eeprom.h @@ -0,0 +1,87 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_MBOARD_EEPROM_H +#define INCLUDED_UHD_USRP_MBOARD_EEPROM_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#ifdef __cplusplus +#include <uhd/usrp/mboard_eeprom.hpp> +#include <string> + +struct uhd_mboard_eeprom_t { + uhd::usrp::mboard_eeprom_t mboard_eeprom_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_mboard_eeprom_t; +#endif + +//! A C-level interface for interacting with a USRP motherboard's EEPROM +/*! + * See uhd::usrp::mboard_eeprom_t for more details. + * + * NOTE: Using a handle before passing it into uhd_mboard_eeprom_make() will + * result in undefined behavior. + */ +typedef struct uhd_mboard_eeprom_t* uhd_mboard_eeprom_handle; + +//! Create a handle for working with a USRP motherboard EEPROM +UHD_API uhd_error uhd_mboard_eeprom_make( + uhd_mboard_eeprom_handle* h +); + +//! Free a USRP motherboard EEPROM handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_mboard_eeprom_free( + uhd_mboard_eeprom_handle* h +); + +//! Get the value associated with the given EEPROM key +UHD_API uhd_error uhd_mboard_eeprom_get_value( + uhd_mboard_eeprom_handle h, + const char* key, + char* value_out, + size_t strbuffer_len +); + +//! Set the value for the given EEPROM key +UHD_API uhd_error uhd_mboard_eeprom_set_value( + uhd_mboard_eeprom_handle h, + const char* key, + const char* value +); + +//! Get the last error recorded by the handle +UHD_API uhd_error uhd_mboard_eeprom_last_error( + uhd_mboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_USRP_MBOARD_EEPROM_H */ diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 7ac38f84b..cc392542f 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012,2014 Ettus Research LLC +// Copyright 2010-2012,2014-2015 Ettus Research LLC // // 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 @@ -22,12 +22,15 @@ #define UHD_USRP_MULTI_USRP_REF_SOURCES_API #define UHD_USRP_MULTI_USRP_GET_RATES_API #define UHD_USRP_MULTI_USRP_FRONTEND_CAL_API +#define UHD_USRP_MULTI_USRP_FRONTEND_IQ_AUTO_API #define UHD_USRP_MULTI_USRP_COMMAND_TIME_API #define UHD_USRP_MULTI_USRP_BW_RANGE_API #define UHD_USRP_MULTI_USRP_USER_REGS_API #define UHD_USRP_MULTI_USRP_GET_USRP_INFO_API #define UHD_USRP_MULTI_USRP_NORMALIZED_GAIN #define UHD_USRP_MULTI_USRP_GPIO_API +#define UHD_USRP_MULTI_USRP_REGISTER_API +#define UHD_USRP_MULTI_USRP_FILTER_API #include <uhd/config.hpp> #include <uhd/device.hpp> @@ -37,6 +40,7 @@ #include <uhd/types/tune_request.hpp> #include <uhd/types/tune_result.hpp> #include <uhd/types/sensors.hpp> +#include <uhd/types/filters.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_iface.hpp> #include <boost/shared_ptr.hpp> @@ -157,6 +161,11 @@ public: * If the specified rate is not available, this method will throw. * On other devices, this method notifies the software of the rate, * but requires the the user has made the necessary hardware change. + * + * If the device has an 'auto clock rate' setting (e.g. B200, see also + * \ref b200_auto_mcr), this will get disabled and the clock rate will be + * fixed to \p rate. + * * \param rate the new master clock rate in Hz * \param mboard the motherboard index 0 to M-1 */ @@ -492,6 +501,34 @@ public: } /*! + * Set the normalized RX gain value. + * + * The normalized gain is a value in [0, 1], where 0 is the + * smallest gain value available, and 1 is the largest, independent + * of the device. In between, gains are linearly interpolated. + * + * Check the individual device manual for notes on the gain range. + * + * Note that it is not possible to specify a gain name for + * this function, it will always set the overall gain. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_rx_gain(double gain, size_t chan = 0) = 0; + + /*! + * Enable or disable the RX AGC module. + * Once this module is enabled manual gain settings will be ignored. + * The AGC will start in a default configuration which should be good for most use cases. + * Device specific configuration parameters can be found in the property tree. + * \param enable Enable or Disable the AGC + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_agc(bool enable, size_t chan = 0) = 0; + + /*! * Get the RX gain value for the specified gain element. * For an empty name, sum across all gain elements. * \param name the name of the gain element @@ -506,6 +543,18 @@ public: } /*! + * Return the normalized RX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_rx_gain(size_t chan = 0) = 0; + + /*! * Get the RX gain range for the specified gain element. * For an empty name, calculate the overall gain range. * \param name the name of the gain element @@ -617,6 +666,14 @@ public: virtual void set_rx_dc_offset(const std::complex<double> &offset, size_t chan = ALL_CHANS) = 0; /*! + * Enable/disable the automatic IQ imbalance correction. + * + * \param enb true to enable automatic IQ balance correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance(const bool enb, size_t chan) = 0; + + /*! * Set the RX frontend IQ imbalance correction. * Use this to adjust the magnitude and phase of I and Q. * @@ -730,6 +787,18 @@ public: } /*! + * Set the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion on normalized + * gains. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_tx_gain(double gain, size_t chan = 0) = 0; + + /*! * Get the TX gain value for the specified gain element. * For an empty name, sum across all gain elements. * \param name the name of the gain element @@ -744,6 +813,18 @@ public: } /*! + * Return the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_tx_gain(size_t chan = 0) = 0; + + /*! * Get the TX gain range for the specified gain element. * For an empty name, calculate the overall gain range. * \param name the name of the gain element @@ -895,6 +976,80 @@ public: */ virtual boost::uint32_t get_gpio_attr(const std::string &bank, const std::string &attr, const size_t mboard = 0) = 0; + /******************************************************************* + * Register IO methods + ******************************************************************/ + struct register_info_t { + size_t bitwidth; + bool readable; + bool writable; + }; + + /*! + * Enumerate the full paths of all low-level USRP registers accessible to read/write + * \param mboard the motherboard index 0 to M-1 + * \return a vector of register paths + */ + virtual std::vector<std::string> enumerate_registers(const size_t mboard = 0) = 0; + + /*! + * Get more information about a low-level device register + * \param path the full path to the register + * \param mboard the motherboard index 0 to M-1 + * \return the info struct which contains the bitwidth and read-write access information + */ + virtual register_info_t get_register_info(const std::string &path, const size_t mboard = 0) = 0; + + /*! + * Write a low-level register field for a register in the USRP hardware + * \param path the full path to the register + * \param field the identifier of bitfield to be written (all other bits remain unchanged) + * \param value the value to write to the register field + * \param mboard the motherboard index 0 to M-1 + */ + virtual void write_register(const std::string &path, const boost::uint32_t field, const boost::uint64_t value, const size_t mboard = 0) = 0; + + /*! + * Read a low-level register field from a register in the USRP hardware + * \param path the full path to the register + * \param field the identifier of bitfield to be read + * \param mboard the motherboard index 0 to M-1 + * \return the value of the register field + */ + virtual boost::uint64_t read_register(const std::string &path, const boost::uint32_t field, const size_t mboard = 0) = 0; + + /******************************************************************* + * Filter API methods + ******************************************************************/ + + /*! + * Enumerate the available filters in the signal path. + * \param search_mask + * \parblock + * Select only certain filter names by specifying this search mask. + * + * E.g. if search mask is set to "rx_frontends/A" only filter names including that string will be returned. + * \endparblock + * \return a vector of strings representing the selected filter names. + */ + virtual std::vector<std::string> get_filter_names(const std::string &search_mask = "") = 0; + + /*! + * Return the filter object for the given name. + * \param path the name of the filter as returned from get_filter_names(). + * \return a filter_info_base::sptr. + */ + virtual filter_info_base::sptr get_filter(const std::string &path) = 0; + + /*! + * Write back a filter obtained by get_filter() to the signal path. + * This filter can be a modified version of the originally returned one. + * The information about Rx or Tx is contained in the path parameter. + * \param path the name of the filter as returned from get_filter_names(). + * \param filter the filter_info_base::sptr of the filter object to be written + */ + virtual void set_filter(const std::string &path, filter_info_base::sptr filter) = 0; + }; }} diff --git a/host/include/uhd/usrp/subdev_spec.h b/host/include/uhd/usrp/subdev_spec.h new file mode 100644 index 000000000..f847181b7 --- /dev/null +++ b/host/include/uhd/usrp/subdev_spec.h @@ -0,0 +1,137 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_SUBDEV_SPEC_H +#define INCLUDED_UHD_USRP_SUBDEV_SPEC_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#include <stdbool.h> + +//! Subdevice specification +typedef struct { + // Daughterboard slot name + char* db_name; + //! Subdevice name + char* sd_name; +} uhd_subdev_spec_pair_t; + +#ifdef __cplusplus +#include <uhd/usrp/subdev_spec.hpp> +#include <string> + +struct uhd_subdev_spec_t { + uhd::usrp::subdev_spec_t subdev_spec_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_subdev_spec_t; +#endif + +//! A C-level interface for working with a list of subdevice specifications +/*! + * See uhd::usrp::subdev_spec_t for more details. + * + * NOTE: Using a handle before passing it into uhd_subdev_spec_make() will result in + * undefined behavior. + */ +typedef struct uhd_subdev_spec_t* uhd_subdev_spec_handle; + +//! Safely destroy any memory created in the generation of a uhd_subdev_spec_pair_t +UHD_API uhd_error uhd_subdev_spec_pair_free( + uhd_subdev_spec_pair_t *subdev_spec_pair +); + +//! Check to see if two subdevice specifications are equal +UHD_API uhd_error uhd_subdev_spec_pairs_equal( + const uhd_subdev_spec_pair_t* first, + const uhd_subdev_spec_pair_t* second, + bool *result_out +); + +//! Create a handle for a list of subdevice specifications +UHD_API uhd_error uhd_subdev_spec_make( + uhd_subdev_spec_handle* h, + const char* markup +); + +//! Safely destroy a subdevice specification handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_subdev_spec_free( + uhd_subdev_spec_handle* h +); + +//! Check how many subdevice specifications are in this list +UHD_API uhd_error uhd_subdev_spec_size( + uhd_subdev_spec_handle h, + size_t *size_out +); + +//! Add a subdevice specification to this list +UHD_API uhd_error uhd_subdev_spec_push_back( + uhd_subdev_spec_handle h, + const char* markup +); + +//! Get the subdevice specification at the given index +UHD_API uhd_error uhd_subdev_spec_at( + uhd_subdev_spec_handle h, + size_t num, + uhd_subdev_spec_pair_t *subdev_spec_pair_out +); + +//! Get a string representation of the given list +UHD_API uhd_error uhd_subdev_spec_to_pp_string( + uhd_subdev_spec_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get a markup string representation of the given list +UHD_API uhd_error uhd_subdev_spec_to_string( + uhd_subdev_spec_handle h, + char* string_out, + size_t strbuffer_len +); + +//! Get the last error recorded by the given handle +UHD_API uhd_error uhd_subdev_spec_last_error( + uhd_subdev_spec_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} + +UHD_API uhd::usrp::subdev_spec_pair_t uhd_subdev_spec_pair_c_to_cpp( + const uhd_subdev_spec_pair_t* subdev_spec_pair_c +); + +UHD_API void uhd_subdev_spec_pair_cpp_to_c( + const uhd::usrp::subdev_spec_pair_t &subdev_spec_pair_cpp, + uhd_subdev_spec_pair_t *subdev_spec_pair_c +); +#endif + +#endif /* INCLUDED_UHD_USRP_SUBDEV_SPEC_H */ diff --git a/host/include/uhd/usrp/usrp.h b/host/include/uhd/usrp/usrp.h new file mode 100644 index 000000000..1bde694b4 --- /dev/null +++ b/host/include/uhd/usrp/usrp.h @@ -0,0 +1,1152 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_H +#define INCLUDED_UHD_USRP_H + +#include <uhd/config.h> +#include <uhd/error.h> +#include <uhd/types/metadata.h> +#include <uhd/types/ranges.h> +#include <uhd/types/sensors.h> +#include <uhd/types/string_vector.h> +#include <uhd/types/tune_request.h> +#include <uhd/types/tune_result.h> +#include <uhd/types/usrp_info.h> +#include <uhd/usrp/mboard_eeprom.h> +#include <uhd/usrp/dboard_eeprom.h> +#include <uhd/usrp/subdev_spec.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include <time.h> + +//! Register info +typedef struct { + size_t bitwidth; + bool readable; + bool writable; +} uhd_usrp_register_info_t; + +/* + * Streamers + */ + +//! A struct of parameters to construct a stream. +/*! + * See uhd::stream_args_t for more details. + */ +typedef struct { + //! Format of host memory + char* cpu_format; + //! Over-the-wire format + char* otw_format; + //! Other stream args + char* args; + //! Array that lists channels + size_t* channel_list; + //! Number of channels + int n_channels; +} uhd_stream_args_t; + +//! How streaming is issued to the device +/*! + * See uhd::stream_cmd_t for more details. + */ +typedef enum { + //! Stream samples indefinitely + UHD_STREAM_MODE_START_CONTINUOUS = 97, + //! End continuous streaming + UHD_STREAM_MODE_STOP_CONTINUOUS = 111, + //! Stream some number of samples and finish + UHD_STREAM_MODE_NUM_SAMPS_AND_DONE = 100, + //! Stream some number of samples but expect more + UHD_STREAM_MODE_NUM_SAMPS_AND_MORE = 109 +} uhd_stream_mode_t; + +//! Define how device streams to host +/*! + * See uhd::stream_cmd_t for more details. + */ +typedef struct { + //! How streaming is issued to the device + uhd_stream_mode_t stream_mode; + //! Number of samples + size_t num_samps; + //! Stream now? + bool stream_now; + //! If not now, then full seconds into future to stream + time_t time_spec_full_secs; + //! If not now, then fractional seconds into future to stream + double time_spec_frac_secs; +} uhd_stream_cmd_t; + +struct uhd_rx_streamer; +struct uhd_tx_streamer; + +//! C-level interface for working with an RX streamer +/*! + * See uhd::rx_streamer for more details. + */ +typedef struct uhd_rx_streamer* uhd_rx_streamer_handle; + +//! C-level interface for working with a TX streamer +/*! + * See uhd::tx_streamer for more details. + */ +typedef struct uhd_tx_streamer* uhd_tx_streamer_handle; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * RX Streamer + */ + +//! Create an RX streamer handle. +/*! + * NOTE: Using this streamer before passing it into uhd_usrp_get_rx_stream() + * will result in undefined behavior. + */ +UHD_API uhd_error uhd_rx_streamer_make( + uhd_rx_streamer_handle *h +); + +//! Free an RX streamer handle. +/*! + * NOTE: Using a streamer after passing it into this function will result + * in a segmentation fault. + */ +UHD_API uhd_error uhd_rx_streamer_free( + uhd_rx_streamer_handle *h +); + +//! Get the number of channels associated with this streamer +UHD_API uhd_error uhd_rx_streamer_num_channels( + uhd_rx_streamer_handle h, + size_t *num_channels_out +); + +//! Get the max number of samples per buffer per packet +UHD_API uhd_error uhd_rx_streamer_max_num_samps( + uhd_rx_streamer_handle h, + size_t *max_num_samps_out +); + +//! Receive buffers containing samples into the given RX streamer +/*! + * See uhd::rx_streamer::recv() for more details. + * + * \param h RX streamer handle + * \param buffs pointer to buffers in which to receive samples + * \param samps_per_buffer max number of samples per buffer + * \param md handle to RX metadata in which to receive results + * \param timeout timeout in seconds to wait for a packet + * \param one_packet send a single packet + * \param items_recvd pointer to output variable for number of samples received + */ +UHD_API uhd_error uhd_rx_streamer_recv( + uhd_rx_streamer_handle h, + void** buffs, + size_t samps_per_buff, + uhd_rx_metadata_handle *md, + double timeout, + bool one_packet, + size_t *items_recvd +); + +//! Issue the given stream command +/*! + * See uhd::rx_streamer::issue_stream_cmd() for more details. + */ +UHD_API uhd_error uhd_rx_streamer_issue_stream_cmd( + uhd_rx_streamer_handle h, + const uhd_stream_cmd_t *stream_cmd +); + +//! Get the last error reported by the RX streamer +/*! + * NOTE: This will overwrite the string currently in error_out before + * using it to return its error. + * + * \param h RX streamer handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer size + */ +UHD_API uhd_error uhd_rx_streamer_last_error( + uhd_rx_streamer_handle h, + char* error_out, + size_t strbuffer_len +); + +/* + * TX Streamer + */ + +//! Create an TX streamer handle. +/*! + * NOTE: Using this streamer before passing it into uhd_usrp_get_tx_stream() + * will result in undefined behavior. + */ +UHD_API uhd_error uhd_tx_streamer_make( + uhd_tx_streamer_handle *h +); + +//! Free an TX streamer handle. +/*! + * NOTE: Using a streamer after passing it into this function will result + * in a segmentation fault. + */ +UHD_API uhd_error uhd_tx_streamer_free( + uhd_tx_streamer_handle *h +); + +//! Get the number of channels associated with this streamer +UHD_API uhd_error uhd_tx_streamer_num_channels( + uhd_tx_streamer_handle h, + size_t *num_channels_out +); + +//! Get the max number of samples per buffer per packet +UHD_API uhd_error uhd_tx_streamer_max_num_samps( + uhd_tx_streamer_handle h, + size_t *max_num_samps_out +); + +//! Send buffers containing samples described by the metadata +/*! + * See uhd::tx_streamer::send() for more details. + * + * \param h TX streamer handle + * \param buffs pointer to buffers containing samples to send + * \param samps_per_buffer max number of samples per buffer + * \param md handle to TX metadata + * \param timeout timeout in seconds to wait for a packet + * \param items_sent pointer to output variable for number of samples send + */ +UHD_API uhd_error uhd_tx_streamer_send( + uhd_tx_streamer_handle h, + const void **buffs, + size_t samps_per_buff, + uhd_tx_metadata_handle *md, + double timeout, + size_t *items_sent +); + +//! Receive an asynchronous message from this streamer +/*! + * See uhd::tx_streamer::recv_async_msg() for more details. + */ +UHD_API uhd_error uhd_tx_streamer_recv_async_msg( + uhd_tx_streamer_handle h, + uhd_async_metadata_handle *md, + double timeout, + bool *valid +); + +//! Get the last error reported by the TX streamer +/*! + * NOTE: This will overwrite the string currently in error_out before + * using it to return its error. + * + * \param h TX streamer handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer size + */ +UHD_API uhd_error uhd_tx_streamer_last_error( + uhd_tx_streamer_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +/**************************************************************************** + * Public Datatypes for USRP / streamer handling. + ***************************************************************************/ +struct uhd_usrp; + +//! C-level interface for working with a USRP device +/* + * See uhd::usrp::multi_usrp for more details. + * + * NOTE: You must pass this handle into uhd_usrp_make before using it. + */ +typedef struct uhd_usrp* uhd_usrp_handle; + +/**************************************************************************** + * USRP Make / Free API calls + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +//! Find all connected USRP devices. +/*! + * See uhd::device::find() for more details. + */ +UHD_API uhd_error uhd_usrp_find( + const char* args, + uhd_string_vector_handle *strings_out +); + +//! Create a USRP handle. +/*! + * \param h the handle + * \param args device args (e.g. "type=x300") + */ +UHD_API uhd_error uhd_usrp_make( + uhd_usrp_handle *h, + const char *args +); + +//! Safely destroy the USRP object underlying the handle. +/*! + * NOTE: Attempting to use a USRP handle after passing it into this function + * will result in a segmentation fault. + */ +UHD_API uhd_error uhd_usrp_free( + uhd_usrp_handle *h +); + +//! Get the last error reported by the USRP handle +UHD_API uhd_error uhd_usrp_last_error( + uhd_usrp_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Create RX streamer from a USRP handle and given stream args +UHD_API uhd_error uhd_usrp_get_rx_stream( + uhd_usrp_handle h, + uhd_stream_args_t *stream_args, + uhd_rx_streamer_handle h_out +); + +//! Create TX streamer from a USRP handle and given stream args +UHD_API uhd_error uhd_usrp_get_tx_stream( + uhd_usrp_handle h, + uhd_stream_args_t *stream_args, + uhd_tx_streamer_handle h_out +); + +/**************************************************************************** + * multi_usrp API calls + ***************************************************************************/ + +//! Get RX info from the USRP device +/*! + * NOTE: After calling this function, uhd_usrp_rx_info_free() must be called on info_out. + */ +UHD_API uhd_error uhd_usrp_get_rx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_rx_info_t *info_out +); + +//! Get TX info from the USRP device +/*! + * NOTE: After calling this function, uhd_usrp_tx_info_free() must be called on info_out. + */ +UHD_API uhd_error uhd_usrp_get_tx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_tx_info_t *info_out +); + +/**************************************************************************** + * Motherboard methods + ***************************************************************************/ + +//! Set the master clock rate. +/*! + * See uhd::usrp::multi_usrp::set_master_clock_rate() for more details. + */ +UHD_API uhd_error uhd_usrp_set_master_clock_rate( + uhd_usrp_handle h, + double rate, + size_t mboard +); + +//! Get the master clock rate. +/*! + * See uhd::usrp::multi_usrp::get_master_clock_rate() for more details. + */ +UHD_API uhd_error uhd_usrp_get_master_clock_rate( + uhd_usrp_handle h, + size_t mboard, + double *clock_rate_out +); + +//! Get a pretty-print representation of the USRP device. +/*! + * See uhd::usrp::multi_usrp::get_pp_string() for more details. + */ +UHD_API uhd_error uhd_usrp_get_pp_string( + uhd_usrp_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the motherboard name for the given device +/*! + * See uhd::usrp::multi_usrp::get_mboard_name() for more details. + */ +UHD_API uhd_error uhd_usrp_get_mboard_name( + uhd_usrp_handle h, + size_t mboard, + char* mboard_name_out, + size_t strbuffer_len +); + +//! Get the USRP device's current internal time +/*! + * See uhd::usrp::multi_usrp::get_time_now() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_now( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +); + +//! Get the time when this device's last PPS pulse occurred +/*! + * See uhd::usrp::multi_usrp::get_time_last_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_last_pps( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +); + +//! Set the USRP device's time +/*! + * See uhd::usrp::multi_usrp::set_time_now() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_now( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +); + +//! Set the USRP device's time to the given value upon the next PPS detection +/*! + * See uhd::usrp::multi_usrp::set_time_next_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_next_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +); + +//! Synchronize the time across all motherboards +/*! + * See uhd::usrp::multi_usrp::set_time_unknown_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_unknown_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs +); + +//! Are all motherboard times synchronized? +UHD_API uhd_error uhd_usrp_get_time_synchronized( + uhd_usrp_handle h, + bool *result_out +); + +//! Set the time at which timed commands will take place +/*! + * See uhd::usrp::multi_usrp::set_command_time() for more details. + */ +UHD_API uhd_error uhd_usrp_set_command_time( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +); + +//! Clear the command time so that commands are sent ASAP +UHD_API uhd_error uhd_usrp_clear_command_time( + uhd_usrp_handle h, + size_t mboard +); + +//! Set the time source for the given device +/*! + * See uhd::usrp::multi_usrp::set_time_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_source( + uhd_usrp_handle h, + const char* time_source, + size_t mboard +); + +//! Get the time source for the given device +/*! + * See uhd::usrp::multi_usrp::get_time_source() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_source( + uhd_usrp_handle h, + size_t mboard, + char* time_source_out, + size_t strbuffer_len +); + +//! Get a list of time sources for the given device +UHD_API uhd_error uhd_usrp_get_time_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *time_sources_out +); + +//! Set the given device's clock source +/*! + * See uhd::usrp::multi_usrp::set_clock_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_clock_source( + uhd_usrp_handle h, + const char* clock_source, + size_t mboard +); + +//! Get the given device's clock source +/*! + * See uhd::usrp::multi_usrp::get_clock_source() for more details. + */ +UHD_API uhd_error uhd_usrp_get_clock_source( + uhd_usrp_handle h, + size_t mboard, + char* clock_source_out, + size_t strbuffer_len +); + +//! Get a list of clock sources for the given device +UHD_API uhd_error uhd_usrp_get_clock_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *clock_sources_out +); + +//! Enable or disable sending the clock source to an output connector +/*! + * See uhd::usrp::set_clock_source_out() for more details. + */ +UHD_API uhd_error uhd_usrp_set_clock_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +); + +//! Enable or disable sending the time source to an output connector +/*! + * See uhd::usrp::set_time_source_out() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +); + +//! Get the number of devices associated with the given USRP handle +UHD_API uhd_error uhd_usrp_get_num_mboards( + uhd_usrp_handle h, + size_t *num_mboards_out +); + +//! Get the value associated with the given sensor name +UHD_API uhd_error uhd_usrp_get_mboard_sensor( + uhd_usrp_handle h, + const char* name, + size_t mboard, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get a list of motherboard sensors for the given device +UHD_API uhd_error uhd_usrp_get_mboard_sensor_names( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *mboard_sensor_names_out +); + +//! Perform a write on a user configuration register bus +/*! + * See uhd::usrp::multi_usrp::set_user_register() for more details. + */ +UHD_API uhd_error uhd_usrp_set_user_register( + uhd_usrp_handle h, + uint8_t addr, + uint32_t data, + size_t mboard +); + +/**************************************************************************** + * EEPROM access methods + ***************************************************************************/ + +//! Get a handle for the given motherboard's EEPROM +UHD_API uhd_error uhd_usrp_get_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +); + +//! Set values in the given motherboard's EEPROM +UHD_API uhd_error uhd_usrp_set_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +); + +//! Get a handle for the given device's daughterboard EEPROM +UHD_API uhd_error uhd_usrp_get_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +); + +//! Set values in the given daughterboard's EEPROM +UHD_API uhd_error uhd_usrp_set_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +); + +/**************************************************************************** + * RX methods + ***************************************************************************/ + +//! Map the given device's RX frontend to a channel +/*! + * See uhd::usrp::multi_usrp::set_rx_subdev_spec() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +); + +//! Get the RX frontend specfication for the given device +UHD_API uhd_error uhd_usrp_get_rx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +); + +//! Get the number of RX channels for the given handle +UHD_API uhd_error uhd_usrp_get_rx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +); + +//! Get the name for the RX frontend +UHD_API uhd_error uhd_usrp_get_rx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* rx_subdev_name_out, + size_t strbuffer_len +); + +//! Set the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_set_rx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +); + +//! Get the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_get_rx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +); + +//! Get a range of possible RX rates for the given channel +UHD_API uhd_error uhd_usrp_get_rx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +); + +//! Set the given channel's center RX frequency +UHD_API uhd_error uhd_usrp_set_rx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +); + +//! Get the given channel's center RX frequency +UHD_API uhd_error uhd_usrp_get_rx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +); + +//! Get all possible center frequency ranges for the given channel +/*! + * See uhd::usrp::multi_usrp::get_rx_freq_range() for more details. + */ +UHD_API uhd_error uhd_usrp_get_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Get all possible RF frequency ranges for the given channel's RX RF frontend +UHD_API uhd_error uhd_usrp_get_fe_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Set the RX gain for the given channel and name +UHD_API uhd_error uhd_usrp_set_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +); + +//! Set the normalized RX gain [0.0, 1.0] for the given channel +/*! + * See uhd::usrp::multi_usrp::set_normalized_rx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_set_normalized_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +); + +//! Enable or disable the given channel's RX AGC module +/*! + * See uhd::usrp::multi_usrp::set_rx_agc() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_agc( + uhd_usrp_handle h, + bool enable, + size_t chan +); + +//! Get the given channel's RX gain +UHD_API uhd_error uhd_usrp_get_rx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +); + +//! Get the given channel's normalized RX gain [0.0, 1.0] +/*! + * See uhd::usrp::multi_usrp::get_normalized_rx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_get_normalized_rx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +); + +//! Get all possible gain ranges for the given channel and name +UHD_API uhd_error uhd_usrp_get_rx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +); + +//! Get a list of RX gain names for the given channel +UHD_API uhd_error uhd_usrp_get_rx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +); + +//! Set the RX antenna for the given channel +UHD_API uhd_error uhd_usrp_set_rx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +); + +//! Get the RX antenna for the given channel +UHD_API uhd_error uhd_usrp_get_rx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +); + +//! Get a list of RX antennas associated with the given channels +UHD_API uhd_error uhd_usrp_get_rx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +); + +//! Get a list of RX sensors associated with the given channels +UHD_API uhd_error uhd_usrp_get_rx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +); + +//! Set the bandwidth for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_set_rx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +); + +//! Get the bandwidth for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_get_rx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +); + +//! Get all possible bandwidth ranges for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_get_rx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +); + +//! Get the value for the given RX sensor +UHD_API uhd_error uhd_usrp_get_rx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +); + +//! Enable or disable RX DC offset correction for the given channel +/*! + * See uhd::usrp::multi_usrp::set_rx_dc_offset() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +//! Enable or disable RX IQ imbalance correction for the given channel +UHD_API uhd_error uhd_usrp_set_rx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +/**************************************************************************** + * TX methods + ***************************************************************************/ + +//! Map the given device's TX frontend to a channel +/*! + * See uhd::usrp::multi_usrp::set_tx_subdev_spec() for more details. + */ +UHD_API uhd_error uhd_usrp_set_tx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +); + +//! Get the TX frontend specfication for the given device +UHD_API uhd_error uhd_usrp_get_tx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +); + +//! Get the number of TX channels for the given handle +UHD_API uhd_error uhd_usrp_get_tx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +); + +//! Get the name for the RX frontend +UHD_API uhd_error uhd_usrp_get_tx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* tx_subdev_name_out, + size_t strbuffer_len +); + +//! Set the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_set_tx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +); + +//! Get the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_get_tx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +); + +//! Get a range of possible RX rates for the given channel +UHD_API uhd_error uhd_usrp_get_tx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +); + +//! Set the given channel's center TX frequency +UHD_API uhd_error uhd_usrp_set_tx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +); + +//! Get the given channel's center TX frequency +UHD_API uhd_error uhd_usrp_get_tx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +); + +//! Get all possible center frequency ranges for the given channel +/*! + * See uhd::usrp::multi_usrp::get_rx_freq_range() for more details. + */ +UHD_API uhd_error uhd_usrp_get_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Get all possible RF frequency ranges for the given channel's TX RF frontend +UHD_API uhd_error uhd_usrp_get_fe_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Set the TX gain for the given channel and name +UHD_API uhd_error uhd_usrp_set_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +); + +//! Set the normalized TX gain [0.0, 1.0] for the given channel +/*! + * See uhd::usrp::multi_usrp::set_normalized_tx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_set_normalized_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +); + +//! Get all possible gain ranges for the given channel and name +UHD_API uhd_error uhd_usrp_get_tx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +); + +//! Get the given channel's RX gain +UHD_API uhd_error uhd_usrp_get_tx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +); + +//! Get the given channel's normalized TX gain [0.0, 1.0] +/*! + * See uhd::usrp::multi_usrp::get_normalized_tx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_get_normalized_tx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +); + +//! Get a list of TX gain names for the given channel +UHD_API uhd_error uhd_usrp_get_tx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +); + +//! Set the TX antenna for the given channel +UHD_API uhd_error uhd_usrp_set_tx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +); + +//! Get the TX antenna for the given channel +UHD_API uhd_error uhd_usrp_get_tx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +); + +//! Get a list of tx antennas associated with the given channels +UHD_API uhd_error uhd_usrp_get_tx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +); + +//! Set the bandwidth for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_set_tx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +); + +//! Get the bandwidth for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_get_tx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +); + +//! Get all possible bandwidth ranges for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_get_tx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +); + +//! Get the value for the given TX sensor +UHD_API uhd_error uhd_usrp_get_tx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get a list of TX sensors associated with the given channels +UHD_API uhd_error uhd_usrp_get_tx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +); + +//! Enable or disable TX DC offset correction for the given channel +/*! + * See uhd::usrp::multi_usrp::set_tx_dc_offset() for more details. + */ +UHD_API uhd_error uhd_usrp_set_tx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +//! Enable or disable TX IQ imbalance correction for the given channel +UHD_API uhd_error uhd_usrp_set_tx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +/**************************************************************************** + * GPIO methods + ***************************************************************************/ + +//! Get a list of GPIO banks associated with the given channels +UHD_API uhd_error uhd_usrp_get_gpio_banks( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *gpio_banks_out +); + +//! Set a GPIO attribute for a given GPIO bank +/*! + * See uhd::usrp::multi_usrp::set_gpio_attr() for more details. + */ +UHD_API uhd_error uhd_usrp_set_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + uint32_t value, + uint32_t mask, + size_t mboard +); + +//! Get a GPIO attribute on a particular GPIO bank +/*! + * See uhd::usrp::multi_usrp::get_gpio_attr() for more details. + */ +UHD_API uhd_error uhd_usrp_get_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + size_t mboard, + uint32_t *attr_out +); + +//! Enumerate the full paths of USRP registers available for read/write +UHD_API uhd_error uhd_usrp_enumerate_registers( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *registers_out +); + +//! Get more information about a low-level device register +UHD_API uhd_error uhd_usrp_get_register_info( + uhd_usrp_handle h, + const char* path, + size_t mboard, + uhd_usrp_register_info_t *register_info_out +); + +//! Write a low-level register field for a device register in the USRP hardware +UHD_API uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + uint64_t value, + size_t mboard +); + +//! Read a low-level register field from a device register in the USRP hardware +UHD_API uhd_error uhd_usrp_read_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + size_t mboard, + uint64_t *value_out +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_USRP_H */ diff --git a/host/include/uhd/usrp_clock/CMakeLists.txt b/host/include/uhd/usrp_clock/CMakeLists.txt index 7cd5aa9d3..518cb7737 100644 --- a/host/include/uhd/usrp_clock/CMakeLists.txt +++ b/host/include/uhd/usrp_clock/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2014 Ettus Research LLC +# Copyright 2014-2015 Ettus Research LLC # # 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 @@ -18,6 +18,12 @@ UHD_INSTALL(FILES octoclock_eeprom.hpp multi_usrp_clock.hpp - DESTINATION ${INCLUDE_DIR}/uhd/octoclock + DESTINATION ${INCLUDE_DIR}/uhd/usrp_clock COMPONENT headers ) + +IF(ENABLE_C_API) + UHD_INSTALL(FILES + usrp_clock.h + ) +ENDIF(ENABLE_C_API) diff --git a/host/include/uhd/usrp_clock/usrp_clock.h b/host/include/uhd/usrp_clock/usrp_clock.h new file mode 100644 index 000000000..39ce04c8c --- /dev/null +++ b/host/include/uhd/usrp_clock/usrp_clock.h @@ -0,0 +1,125 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_CLOCK_H +#define INCLUDED_UHD_USRP_CLOCK_H + +#include <uhd/config.h> +#include <uhd/error.h> +#include <uhd/types/sensors.h> +#include <uhd/types/string_vector.h> + +#include <stdlib.h> +#include <stdint.h> +#include <time.h> + +/**************************************************************************** + * Public Datatypes for USRP clock + ***************************************************************************/ +struct uhd_usrp_clock; + +//! A C-level interface for interacting with an Ettus Research clock device +/*! + * See uhd::usrp_clock::multi_usrp_clock for more details. + * + * NOTE: Attempting to use a handle before passing it into uhd_usrp_clock_make() + * will result in undefined behavior. + */ +typedef struct uhd_usrp_clock* uhd_usrp_clock_handle; + +/**************************************************************************** + * Make / Free API calls + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +//! Find all connected clock devices. +/*! + * See uhd::device::find() for more details. + */ +UHD_API uhd_error uhd_usrp_clock_find( + const char* args, + uhd_string_vector_t *devices_out +); + +//! Create a clock handle. +/*! + * \param h The handle + * \param args Device args (e.g. "addr=192.168.10.3") + */ +UHD_API uhd_error uhd_usrp_clock_make( + uhd_usrp_clock_handle *h, + const char *args +); + +//! Safely destroy the clock object underlying the handle. +/*! + * Note: After calling this, usage of h may cause segmentation faults. + * However, multiple calling of uhd_usrp_free() is safe. + */ +UHD_API uhd_error uhd_usrp_clock_free( + uhd_usrp_clock_handle *h +); + +//! Get last error +UHD_API uhd_error uhd_usrp_clock_last_error( + uhd_usrp_clock_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Get board information in a nice output +UHD_API uhd_error uhd_usrp_clock_get_pp_string( + uhd_usrp_clock_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get number of boards +UHD_API uhd_error uhd_usrp_clock_get_num_boards( + uhd_usrp_clock_handle h, + size_t *num_boards_out +); + +//! Get time +UHD_API uhd_error uhd_usrp_clock_get_time( + uhd_usrp_clock_handle h, + size_t board, + uint32_t *clock_time_out +); + +//! Get sensor +UHD_API uhd_error uhd_usrp_clock_get_sensor( + uhd_usrp_clock_handle h, + const char* name, + size_t board, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get sensor names +UHD_API uhd_error uhd_usrp_clock_get_sensor_names( + uhd_usrp_clock_handle h, + size_t board, + uhd_string_vector_handle *sensor_names_out +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_USRP_CLOCK_H */ diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 6b3d5a581..af6d3ee47 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -42,3 +42,11 @@ UHD_INSTALL(FILES DESTINATION ${INCLUDE_DIR}/uhd/utils COMPONENT headers ) + +IF(ENABLE_C_API) + UHD_INSTALL(FILES + thread_priority.h + DESTINATION ${INCLUDE_DIR}/uhd/utils + COMPONENT headers + ) +ENDIF(ENABLE_C_API) diff --git a/host/include/uhd/utils/dirty_tracked.hpp b/host/include/uhd/utils/dirty_tracked.hpp new file mode 100644 index 000000000..d228a9e65 --- /dev/null +++ b/host/include/uhd/utils/dirty_tracked.hpp @@ -0,0 +1,132 @@ +// +// Copyright 2010-2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP +#define INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP + +namespace uhd{ + /*! + * A class that wraps a data value with a dirty flag + * When the client uses the assignment operator on this object, the object + * automatically dirties itself if the assigned type is not equal the underlying + * data. Data can be cleaned using the mark_clean entry-point. + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + */ + template<typename data_t> + class dirty_tracked { + public: + /*! + * Default ctor: Initialize to default value and dirty + */ + dirty_tracked() : + _data(), //data_t must have a default ctor + _dirty(true) + {} + + /*! + * Initialize to specified value and dirty + */ + dirty_tracked(const data_t& value) : + _data(value), //data_t must have a copy ctor + _dirty(true) + {} + + /*! + * Copy ctor: Assign source to this type + */ + dirty_tracked(const dirty_tracked& source) { + *this = source; + } + + /*! + * Get underlying data + */ + inline const data_t& get() const { + return _data; + } + + /*! + * Has the underlying data changed since the last + * time it was cleaned? + */ + inline bool is_dirty() const { + return _dirty; + } + + /*! + * Mark the underlying data as clean + */ + inline void mark_clean() { + _dirty = false; + } + + /*! + * Mark the underlying data as dirty + */ + inline void force_dirty() { + _dirty = true; + } + + /*! + * Assignment with data. + * Store the specified value and mark it as dirty + * if it is not equal to the underlying data. + */ + inline dirty_tracked& operator=(const data_t& value) + { + if(!(_data == value)) { //data_t must have an equality operator + _dirty = true; + _data = value; //data_t must have an assignment operator + } + return *this; + } + + /*! + * Assignment with dirty tracked type. + * Store the specified value from dirty type and mark it as dirty + * if it is not equal to the underlying data. + * This exists to optimize out an implicit cast from dirty_tracked + * type to data type. + */ + inline dirty_tracked& operator=(const dirty_tracked& source) { + if (!(_data == source._data)) { + _dirty = true; + _data = source._data; + } + return *this; + } + + /*! + * Explicit conversion from this type to data_t + */ + inline operator const data_t&() const { + return get(); + } + + private: + data_t _data; + bool _dirty; + }; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_DIRTY_TRACKED_HPP */ diff --git a/host/include/uhd/utils/math.hpp b/host/include/uhd/utils/math.hpp index 2937ba802..ef7bfb6b7 100644 --- a/host/include/uhd/utils/math.hpp +++ b/host/include/uhd/utils/math.hpp @@ -18,11 +18,11 @@ #ifndef INCLUDED_UHD_UTILS_MATH_HPP #define INCLUDED_UHD_UTILS_MATH_HPP +#include <cmath> #include <uhd/config.hpp> #include <boost/cstdint.hpp> #include <boost/numeric/conversion/bounds.hpp> - namespace uhd { /*! @@ -237,6 +237,16 @@ namespace fp_compare { == fp_compare::fp_compare_delta<double>(rhs, FREQ_COMPARISON_DELTA_HZ)); } + //! Portable log2() + template <typename float_t> UHD_INLINE + float_t log2(float_t x) + { + // C++11 defines std::log2(), when that's universally supported + // we can switch over. + return std::log(x) / std::log(float_t(2)); + } + + } // namespace math } // namespace uhd diff --git a/host/include/uhd/utils/paths.hpp b/host/include/uhd/utils/paths.hpp index 0dbee3446..b97d3a55d 100644 --- a/host/include/uhd/utils/paths.hpp +++ b/host/include/uhd/utils/paths.hpp @@ -39,7 +39,7 @@ namespace uhd { UHD_API std::string get_pkg_path(void); //! Get UHD library paths - std::vector<fs::path> get_module_paths(void); + UHD_API std::vector<fs::path> get_module_paths(void); /*! Return the UHD images directory path. * @@ -58,7 +58,7 @@ namespace uhd { * \param search_paths A comma-separated list of hints for paths to include. * \returns A path string if one is found, or an empty string on failure. */ - UHD_API std::string get_images_dir(const std::string search_paths); + UHD_API std::string get_images_dir(const std::string &search_paths); /*! Return the full path to particular UHD binary image. * @@ -72,7 +72,7 @@ namespace uhd { * \return the full system path to the file * \throw exception uhd::io_error if the file was not found. */ - UHD_API std::string find_image_path(const std::string &image_name, const std::string search_paths = ""); + UHD_API std::string find_image_path(const std::string &image_name, const std::string &search_paths = ""); /*! * Search for the location of a particular UHD utility. @@ -80,14 +80,14 @@ namespace uhd { * \param name the name of the utility to search for * \return the full system path to the given utility */ - UHD_API std::string find_utility(std::string name); + UHD_API std::string find_utility(const std::string &name); /*! * Return an error string recommending the user run the utility. * The error string will include the full path to the utility to run. * \return the message suggesting the use of the named utility. */ - UHD_API std::string print_utility_error(std::string name); + UHD_API std::string print_utility_error(const std::string &name, const std::string &args = ""); } //namespace uhd #endif /* INCLUDED_UHD_UTILS_PATHS_HPP */ diff --git a/host/include/uhd/utils/soft_register.hpp b/host/include/uhd/utils/soft_register.hpp new file mode 100644 index 000000000..9006ab5c7 --- /dev/null +++ b/host/include/uhd/utils/soft_register.hpp @@ -0,0 +1,688 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_SOFT_REGISTER_HPP +#define INCLUDED_UHD_UTILS_SOFT_REGISTER_HPP + +#include <boost/cstdint.hpp> +#include <boost/noncopyable.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/dirty_tracked.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/locks.hpp> +#include <boost/unordered_map.hpp> +#include <boost/tokenizer.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <list> + +/*! \file soft_register.hpp + * Utilities to access and index hardware registers. + * + * This file contains three main utilities: + * - A soft_register wrapper class that can manage a soft-copy, + * do dirty tracking and allow symbolic access to various field + * of a register. + * - A register map class that can own multiple soft registers that + * share the same underlying control interface. + * - A register map database that can be used to collect multiple + * register maps and other databases to create a hierarchy of + * registers that can be accessed using the UHD register API. + */ + +//================================================================== +// Soft Register Definition +//================================================================== + +#define UHD_DEFINE_SOFT_REG_FIELD(name, width, shift) \ + static const uhd::soft_reg_field_t name = (((shift & 0xFF) << 8) | (width & 0xFF)) + +namespace uhd { + +//TODO: These hints were added to boost 1.53. + +/** \brief hint for the branch prediction */ +inline bool likely(bool expr) +{ +#ifdef __GNUC__ + return __builtin_expect(expr, true); +#else + return expr; +#endif + } + +/** \brief hint for the branch prediction */ +inline bool unlikely(bool expr) +{ +#ifdef __GNUC__ + return __builtin_expect(expr, false); +#else + return expr; +#endif +} + +/*! + * A register field is defined as a tuple of the mask and the shift. + * It can be used to make read-modify-write operations more convenient + * For efficiency reasons, it is recommended to always use a constant + * of this type because it will get optimized out by the compiler and + * will result in zero memory overhead + */ +typedef boost::uint32_t soft_reg_field_t; + +namespace soft_reg_field { + inline size_t width(const soft_reg_field_t field) { + return (field & 0xFF); + } + + inline size_t shift(const soft_reg_field_t field) { + return ((field >> 8) & 0xFF); + } + + template<typename data_t> + inline size_t mask(const soft_reg_field_t field) { + static const data_t ONE = static_cast<data_t>(1); + //Behavior for the left shift operation is undefined in C++ + //if the shift amount is >= bitwidth of the datatype + //So we treat that as a special case with a branch predicition hint + if (likely((sizeof(data_t)*8) != width(field))) + return ((ONE<<width(field))-ONE)<<shift(field); + else + return (0-ONE)<<shift(field); + } +} + +class soft_register_base : public boost::noncopyable { +public: + virtual ~soft_register_base() {} + + virtual void initialize(wb_iface& iface, bool sync = false) = 0; + virtual void flush() = 0; + virtual void refresh() = 0; + virtual size_t get_bitwidth() = 0; + virtual bool is_readable() = 0; + virtual bool is_writable() = 0; + + /*! + * Cast the soft_register generic reference to a more specific type + */ + template <typename soft_reg_t> + inline static soft_reg_t& cast(soft_register_base& reg) { + soft_reg_t* ptr = dynamic_cast<soft_reg_t*>(®); + if (ptr) { + return *ptr; + } else { + throw uhd::type_error("failed to cast register to specified type"); + } + } +}; + +enum soft_reg_flush_mode_t { OPTIMIZED_FLUSH, ALWAYS_FLUSH }; + +/*! + * Soft register object that holds offset, soft-copy and the control iface. + * Methods give convenient field-level access to soft-copy and the ability + * to do read-modify-write operations. + */ +template<typename reg_data_t, bool readable, bool writable> +class UHD_API soft_register_t : public soft_register_base { +public: + typedef boost::shared_ptr< soft_register_t<reg_data_t, readable, writable> > sptr; + + //Reserved field. Represents all bits in the register. + UHD_DEFINE_SOFT_REG_FIELD(REGISTER, sizeof(reg_data_t)*8, 0); //[WIDTH-1:0] + + /*! + * Generic constructor for all soft_register types + */ + explicit soft_register_t( + wb_iface::wb_addr_type wr_addr, + wb_iface::wb_addr_type rd_addr, + soft_reg_flush_mode_t mode = ALWAYS_FLUSH): + _iface(NULL), _wr_addr(wr_addr), _rd_addr(rd_addr), _soft_copy(0), _flush_mode(mode) + {} + + /*! + * Constructor for read-only, write-only registers and read-write registers + * with rd_addr == wr_addr + */ + explicit soft_register_t( + wb_iface::wb_addr_type addr, + soft_reg_flush_mode_t mode = ALWAYS_FLUSH): + _iface(NULL), _wr_addr(addr), _rd_addr(addr), _soft_copy(0), _flush_mode(mode) + {} + + /*! + * Initialize the register when the underlying bus is usable. + * Can be optionally synced with hardware. + * NOTE: Memory management of the iface is up to the caller + */ + inline void initialize(wb_iface& iface, bool sync = false) + { + _iface = &iface; + + //Synchronize with hardware. For RW register, flush THEN refresh. + if (sync && writable) flush(); + if (sync && readable) refresh(); + } + + /*! + * Update specified field in the soft-copy with the arg value. + * Performs a read-modify-write operation so all other field are preserved. + * NOTE: This does not write the value to hardware. + */ + inline void set(const soft_reg_field_t field, const reg_data_t value) + { + _soft_copy = (_soft_copy & ~soft_reg_field::mask<reg_data_t>(field)) | + ((value << soft_reg_field::shift(field)) & soft_reg_field::mask<reg_data_t>(field)); + } + + /*! + * Get the value of the specified field from the soft-copy. + * NOTE: This does not read anything from hardware. + */ + inline reg_data_t get(const soft_reg_field_t field) + { + return (_soft_copy & soft_reg_field::mask<reg_data_t>(field)) >> soft_reg_field::shift(field); + } + + /*! + * Write the contents of the soft-copy to hardware. + */ + inline void flush() + { + if (writable && _iface) { + //If optimized flush then poke only if soft copy is dirty + //If flush mode is ALWAYS, the dirty flag should get optimized + //out by the compiler because it is never read + if (_flush_mode == ALWAYS_FLUSH || _soft_copy.is_dirty()) { + if (get_bitwidth() <= 16) { + _iface->poke16(_wr_addr, static_cast<boost::uint16_t>(_soft_copy)); + } else if (get_bitwidth() <= 32) { + _iface->poke32(_wr_addr, static_cast<boost::uint32_t>(_soft_copy)); + } else if (get_bitwidth() <= 64) { + _iface->poke64(_wr_addr, static_cast<boost::uint64_t>(_soft_copy)); + } else { + throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); + } + _soft_copy.mark_clean(); + } + } else { + throw uhd::not_implemented_error("soft_register is not writable."); + } + } + + /*! + * Read the contents of the register from hardware and update the soft copy. + */ + inline void refresh() + { + if (readable && _iface) { + if (get_bitwidth() <= 16) { + _soft_copy = static_cast<reg_data_t>(_iface->peek16(_rd_addr)); + } else if (get_bitwidth() <= 32) { + _soft_copy = static_cast<reg_data_t>(_iface->peek32(_rd_addr)); + } else if (get_bitwidth() <= 64) { + _soft_copy = static_cast<reg_data_t>(_iface->peek64(_rd_addr)); + } else { + throw uhd::not_implemented_error("soft_register only supports up to 64 bits."); + } + _soft_copy.mark_clean(); + } else { + throw uhd::not_implemented_error("soft_register is not readable."); + } + } + + /*! + * Shortcut for a set and a flush. + */ + inline void write(const soft_reg_field_t field, const reg_data_t value) + { + set(field, value); + flush(); + } + + /*! + * Shortcut for refresh and get + */ + inline reg_data_t read(const soft_reg_field_t field) + { + refresh(); + return get(field); + } + + /*! + * Get bitwidth for this register + */ + inline size_t get_bitwidth() + { + static const size_t BITS_IN_BYTE = 8; + return sizeof(reg_data_t) * BITS_IN_BYTE; + } + + /*! + * Is the register readable? + */ + inline bool is_readable() + { + return readable; + } + + /*! + * Is the register writable? + */ + inline bool is_writable() + { + return writable; + } + +private: + wb_iface* _iface; + const wb_iface::wb_addr_type _wr_addr; + const wb_iface::wb_addr_type _rd_addr; + dirty_tracked<reg_data_t> _soft_copy; + const soft_reg_flush_mode_t _flush_mode; +}; + +/*! + * A synchronized soft register object. + * All operations in the synchronized register are serialized. + */ +template<typename reg_data_t, bool readable, bool writable> +class UHD_API soft_register_sync_t : public soft_register_t<reg_data_t, readable, writable> { +public: + typedef boost::shared_ptr< soft_register_sync_t<reg_data_t, readable, writable> > sptr; + + explicit soft_register_sync_t( + wb_iface::wb_addr_type wr_addr, + wb_iface::wb_addr_type rd_addr, + soft_reg_flush_mode_t mode = ALWAYS_FLUSH): + soft_register_t<reg_data_t, readable, writable>(wr_addr, rd_addr, mode), _mutex() + {} + + explicit soft_register_sync_t( + wb_iface::wb_addr_type addr, + soft_reg_flush_mode_t mode = ALWAYS_FLUSH): + soft_register_t<reg_data_t, readable, writable>(addr, mode), _mutex() + {} + + inline void initialize(wb_iface& iface, bool sync = false) + { + boost::lock_guard<boost::mutex> lock(_mutex); + soft_register_t<reg_data_t, readable, writable>::initialize(iface, sync); + } + + inline void set(const soft_reg_field_t field, const reg_data_t value) + { + boost::lock_guard<boost::mutex> lock(_mutex); + soft_register_t<reg_data_t, readable, writable>::set(field, value); + } + + inline reg_data_t get(const soft_reg_field_t field) + { + boost::lock_guard<boost::mutex> lock(_mutex); + return soft_register_t<reg_data_t, readable, writable>::get(field); + } + + inline void flush() + { + boost::lock_guard<boost::mutex> lock(_mutex); + soft_register_t<reg_data_t, readable, writable>::flush(); + } + + inline void refresh() + { + boost::lock_guard<boost::mutex> lock(_mutex); + soft_register_t<reg_data_t, readable, writable>::refresh(); + } + + inline void write(const soft_reg_field_t field, const reg_data_t value) + { + boost::lock_guard<boost::mutex> lock(_mutex); + soft_register_t<reg_data_t, readable, writable>::write(field, value); + } + + inline reg_data_t read(const soft_reg_field_t field) + { + boost::lock_guard<boost::mutex> lock(_mutex); + return soft_register_t<reg_data_t, readable, writable>::read(field); + } + +private: + boost::mutex _mutex; +}; + +/* + * Register Shortcut Formats: + * - soft_reg<bits>_<mode>_t: Soft register object with an unsynchronized soft-copy. + * Thread unsafe but lightweight. Mostly const propagated. + * - soft_reg<bits>_<mode>_sync_t: Soft register object with a synchronized soft-copy. + * Thread safe but with memory/speed overhead. + * where: + * - <bits> = {16, 32 or 64} + * - <mode> = {wo(write-only), rw(read-write) or ro(read-only)} + * + */ + +//16-bit shortcuts +typedef soft_register_t<boost::uint16_t, false, true> soft_reg16_wo_t; +typedef soft_register_t<boost::uint16_t, true, false> soft_reg16_ro_t; +typedef soft_register_t<boost::uint16_t, true, true> soft_reg16_rw_t; +typedef soft_register_sync_t<boost::uint16_t, false, true> soft_reg16_wo_sync_t; +typedef soft_register_sync_t<boost::uint16_t, true, false> soft_reg16_ro_sync_t; +typedef soft_register_sync_t<boost::uint16_t, true, true> soft_reg16_rw_sync_t; +//32-bit shortcuts +typedef soft_register_t<boost::uint32_t, false, true> soft_reg32_wo_t; +typedef soft_register_t<boost::uint32_t, true, false> soft_reg32_ro_t; +typedef soft_register_t<boost::uint32_t, true, true> soft_reg32_rw_t; +typedef soft_register_sync_t<boost::uint32_t, false, true> soft_reg32_wo_sync_t; +typedef soft_register_sync_t<boost::uint32_t, true, false> soft_reg32_ro_sync_t; +typedef soft_register_sync_t<boost::uint32_t, true, true> soft_reg32_rw_sync_t; +//64-bit shortcuts +typedef soft_register_t<boost::uint64_t, false, true> soft_reg64_wo_t; +typedef soft_register_t<boost::uint64_t, true, false> soft_reg64_ro_t; +typedef soft_register_t<boost::uint64_t, true, true> soft_reg64_rw_t; +typedef soft_register_sync_t<boost::uint64_t, false, true> soft_reg64_wo_sync_t; +typedef soft_register_sync_t<boost::uint64_t, true, false> soft_reg64_ro_sync_t; +typedef soft_register_sync_t<boost::uint64_t, true, true> soft_reg64_rw_sync_t; + + +/* + * Usage example + * + //===Define bit width, RW mode, and synchronization using base class=== + class example_reg_t : public soft_reg32_wo_sync_t (or soft_reg32_wo_t) { + public: + //===Define all the fields=== + UHD_DEFINE_SOFT_REG_FIELD(FIELD0, 1, 0); //[0] + UHD_DEFINE_SOFT_REG_FIELD(FIELD1, 15, 1); //[15:1] + UHD_DEFINE_SOFT_REG_FIELD(FIELD2, 16, 16); //[31:16] + + example_reg_t(): //ctor with no args + soft_reg32_wo_t(SR_CORE_EXAMPLE_REG_OFFSET)) //===Bind to offset=== + { + //===Set Initial values=== + set(FIELD0, 0); + set(FIELD1, 1); + set(FIELD2, 0xFFFF); + } + }; //===Full register definition encapsulated in one class=== + + void main() { + example_reg_t reg_obj; + reg_obj.initialize(iface); + reg_obj.write(example_reg_t::FIELD2, 0x1234); + + example_reg_t::sptr reg_sptr = boost::make_shared<example_reg_t>(); + reg_obj->initialize(iface); + reg_obj->write(example_reg_t::FIELD2, 0x1234); + } +*/ +} + +//================================================================== +// Soft Register Map and Database Definition +//================================================================== + +namespace uhd { + +class UHD_API soft_regmap_accessor_t { +public: + typedef boost::shared_ptr<soft_regmap_accessor_t> sptr; + + virtual ~soft_regmap_accessor_t() {}; + virtual soft_register_base& lookup(const std::string& path) const = 0; + virtual std::vector<std::string> enumerate() const = 0; + virtual const std::string& get_name() const = 0; +}; + +/*! + * A regmap is a collection of registers that share the same + * bus (control iface). A regmap must have an identifier. + * A regmap must manage storage for each register. + * The recommended way to use a regmap is to define individual registers + * within the scope of the regmap and instantiate them in the ragmap. + * Soft register object that holds offset, soft-copy and the control iface. + * Methods give convenient field-level access to soft-copy and the ability + * to do read-modify-write operations. + */ +class UHD_API soft_regmap_t : public soft_regmap_accessor_t, public boost::noncopyable { +public: + soft_regmap_t(const std::string& name) : _name(name) {} + virtual ~soft_regmap_t() {}; + + /*! + * Get the name of this register map + */ + virtual inline const std::string& get_name() const { return _name; } + + /*! + * Initialize all registers in this register map using a bus. + * Optionally synchronize the register with hardware. + * The order of initialization is the same as the order in + * which registers were added to the map. + */ + void initialize(wb_iface& iface, bool sync = false) { + boost::lock_guard<boost::mutex> lock(_mutex); + BOOST_FOREACH(soft_register_base* reg, _reglist) { + reg->initialize(iface, sync); + } + } + + /*! + * Flush all registers to hardware. + * The order of writing is the same as the order in + * which registers were added to the map. + */ + void flush() { + boost::lock_guard<boost::mutex> lock(_mutex); + BOOST_FOREACH(soft_register_base* reg, _reglist) { + reg->flush(); + } + } + + /*! + * Refresh all register soft-copies from hardware. + * The order of reading is the same as the order in + * which registers were added to the map. + */ + void refresh() { + boost::lock_guard<boost::mutex> lock(_mutex); + BOOST_FOREACH(soft_register_base* reg, _reglist) { + reg->refresh(); + } + } + + /*! + * Lookup a register object by name. + * If a register with "name" is not found, runtime_error is thrown + */ + virtual soft_register_base& lookup(const std::string& name) const { + regmap_t::const_iterator iter = _regmap.find(name); + if (iter != _regmap.end()) { + return *(iter->second); + } else { + throw uhd::runtime_error("register not found in map: " + name); + } + } + + /*! + * Enumerate all the registers in this map. + * Return fully qualified paths. + */ + virtual std::vector<std::string> enumerate() const { + std::vector<std::string> temp; + BOOST_FOREACH(const regmap_t::value_type& reg, _regmap) { + temp.push_back(_name + "/" + reg.first); + } + return temp; + } + +protected: + enum visibility_t { + PUBLIC, //Is accessible through the soft_regmap_accessor_t interface + PRIVATE //Is NOT accessible through the soft_regmap_accessor_t interface + }; + + /*! + * Add a register to this map with an identifier "name" and visibility + */ + inline void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) { + boost::lock_guard<boost::mutex> lock(_mutex); + if (visible == PUBLIC) { + //Only add to the map if this register is publicly visible + if (not _regmap.insert(regmap_t::value_type(name, ®)).second) { + throw uhd::assertion_error("cannot add two registers with the same name to regmap: " + name); + } + } + _reglist.push_back(®); + } + +private: + typedef boost::unordered_map<std::string, soft_register_base*> regmap_t; + typedef std::list<soft_register_base*> reglist_t; + + const std::string _name; + regmap_t _regmap; //For lookups + reglist_t _reglist; //To maintain order + boost::mutex _mutex; +}; + + +/*! + * A regmap database is a collection of regmaps or other regmap databases + * this allows for efficient encapsulation for multiple registers in a hierarchical + * fashion. + * A regmap_db *does not* manage storage for regmaps. It is simply a wrapper. + */ +class UHD_API soft_regmap_db_t : public soft_regmap_accessor_t, public boost::noncopyable { +public: + typedef boost::shared_ptr<soft_regmap_db_t> sptr; + + /*! + * Use the default constructor if this is the top-level DB + */ + soft_regmap_db_t() : _name("") {} + + /*! + * Use this constructor if this is a nested DB + */ + soft_regmap_db_t(const std::string& name) : _name(name) {} + + /*! + * Get the name of this register map + */ + const std::string& get_name() const { return _name; } + + /*! + * Add a regmap to this map with an identifier "name" and visibility + */ + void add(soft_regmap_t& regmap) { + boost::lock_guard<boost::mutex> lock(_mutex); + _regmaps.push_back(®map); + } + + /*! + * Add a level of regmap_db to this map with an identifier "name" and visibility + */ + void add(soft_regmap_db_t& db) { + boost::lock_guard<boost::mutex> lock(_mutex); + if (&db == this) { + throw uhd::assertion_error("cannot add regmap db to itself" + _name); + } else { + _regmap_dbs.push_back(&db); + } + } + + /*! + * Lookup a register by path. + * A path is defined as a string of "/" separated tokens that scope a register. + * The leaf (last token) is the name of the register + * The token immediately before the leaf is the name of the register map + * If a nested regmap_db is used, the token before the regmap is the db name. + * For every nested db, the path has an additional token. + * For example: + * radio0/spi_regmap/spi_control_reg + */ + soft_register_base& lookup(const std::string& path) const + { + //Turn the slash separated path string into tokens + std::list<std::string> tokens; + BOOST_FOREACH( + const std::string& node, + boost::tokenizer< boost::char_separator<char> >(path, boost::char_separator<char>("/"))) + { + tokens.push_back(node); + } + if ((tokens.size() > 2 && tokens.front() == _name) || //If this is a nested DB + (tokens.size() > 1 && _name == "")) { //If this is a top-level DB + if (_name != "") tokens.pop_front(); + if (tokens.size() == 2) { //2 tokens => regmap/register path + BOOST_FOREACH(const soft_regmap_accessor_t* regmap, _regmaps) { + if (regmap->get_name() == tokens.front()) { + return regmap->lookup(tokens.back()); + } + } + throw uhd::runtime_error("could not find register map: " + path); + } else if (not _regmap_dbs.empty()) { //>2 tokens => <1 or more dbs>/regmap/register + //Reconstruct path from tokens + std::string newpath; + BOOST_FOREACH(const std::string& node, tokens) { + newpath += ("/" + node); + } + //Dispatch path to hierarchical DB + BOOST_FOREACH(const soft_regmap_accessor_t* db, _regmap_dbs) { + try { + return db->lookup(newpath.substr(1)); + } catch (std::exception& e) { + continue; + } + } + } + } + throw uhd::runtime_error("could not find register: " + path); + } + + /*! + * Enumerate the paths of all registers that this DB can access + */ + virtual std::vector<std::string> enumerate() const { + std::vector<std::string> paths; + BOOST_FOREACH(const soft_regmap_accessor_t* regmap, _regmaps) { + const std::vector<std::string>& regs = regmap->enumerate(); + paths.insert(paths.end(), regs.begin(), regs.end()); + } + BOOST_FOREACH(const soft_regmap_accessor_t* db, _regmap_dbs) { + const std::vector<std::string>& regs = db->enumerate(); + paths.insert(paths.end(), regs.begin(), regs.end()); + } + return paths; + } + +private: + typedef std::list<soft_regmap_accessor_t*> db_t; + + const std::string _name; + db_t _regmaps; + db_t _regmap_dbs; + boost::mutex _mutex; +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_SOFT_REGISTER_HPP */ diff --git a/host/include/uhd/utils/thread_priority.h b/host/include/uhd/utils/thread_priority.h new file mode 100644 index 000000000..217d7a1cc --- /dev/null +++ b/host/include/uhd/utils/thread_priority.h @@ -0,0 +1,53 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_THREAD_PRIORITY_H +#define INCLUDED_UHD_UTILS_THREAD_PRIORITY_H + +#include <uhd/config.h> +#include <uhd/error.h> + +#ifdef __cplusplus +extern "C" { +#endif + +static const float uhd_default_thread_priority = 0.5; + +/*! + * Set the scheduling priority on the current thread. + * + * A new thread or calling process should make this call + * with the defailts this to enable realtime scheduling. + * + * A priority of zero corresponds to normal priority. + * Positive priority values are higher than normal. + * Negative priority values are lower than normal. + * + * \param priority a value between -1 and 1 + * \param realtime true to use realtime mode + * \return UHD error code + */ +UHD_API uhd_error uhd_set_thread_priority( + float priority, + bool realtime +); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_UHD_UTILS_THREAD_PRIORITY_H */ diff --git a/host/include/uhd/version.hpp.in b/host/include/uhd/version.hpp.in index 2d578d77e..e2c64812d 100644 --- a/host/include/uhd/version.hpp.in +++ b/host/include/uhd/version.hpp.in @@ -27,7 +27,7 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.8.2-0" +#define UHD_VERSION_ABI_STRING "3.9.0-0" /*! * A macro to check UHD version at compile-time. diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index d8c6fad70..f74af1f29 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -89,12 +89,19 @@ CONFIGURE_FILE( LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/deprecated.cpp ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/stream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/exception.cpp ${CMAKE_CURRENT_SOURCE_DIR}/property_tree.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/error_c.cpp + ) +ENDIF(ENABLE_C_API) + ######################################################################## # Add DLL resource file to Windows build ######################################################################## @@ -103,8 +110,24 @@ IF(MSVC) IF(UHD_VERSION_DEVEL) SET(RC_TRIMMED_VERSION_PATCH "999") ENDIF(UHD_VERSION_DEVEL) + + # Allow a custom .rc template file to be used + IF(CUSTOM_RC_FILE) + IF(IS_ABSOLUTE "${CUSTOM_RC_FILE}") + SET(UHD_RC_IN "${CUSTOM_RC_FILE}") + ELSE() + SET(UHD_RC_IN "${CMAKE_BINARY_DIR}/${CUSTOM_RC_FILE}") + ENDIF(IS_ABSOLUTE "${CUSTOM_RC_FILE}") + MESSAGE(STATUS "") + MESSAGE(STATUS "Using custom RC template: ${UHD_RC_IN}") + MESSAGE(STATUS "") + ELSE() + SET(UHD_RC_IN "${CMAKE_CURRENT_SOURCE_DIR}/uhd.rc.in") + ENDIF(CUSTOM_RC_FILE) + SET(UHD_RC_IN ${UHD_RC_IN} CACHE STRING "uhd.rc template filepath") + CONFIGURE_FILE( - ${CMAKE_CURRENT_SOURCE_DIR}/uhd.rc.in + ${UHD_RC_IN} ${CMAKE_CURRENT_BINARY_DIR}/uhd.rc @ONLY) diff --git a/host/lib/convert/CMakeLists.txt b/host/lib/convert/CMakeLists.txt index 5204c29ea..024c2260b 100644 --- a/host/lib/convert/CMakeLists.txt +++ b/host/lib/convert/CMakeLists.txt @@ -22,40 +22,6 @@ INCLUDE(CheckIncludeFileCXX) MESSAGE(STATUS "") ######################################################################## -# Look for Orc support -######################################################################## -FIND_PACKAGE(ORC) - -IF(NOT ORCC_EXECUTABLE) - FIND_PROGRAM(ORCC_EXECUTABLE orcc) -ENDIF() - -LIBUHD_REGISTER_COMPONENT("ORC" ENABLE_ORC ON "ENABLE_LIBUHD;ORC_FOUND;ORCC_EXECUTABLE" OFF) - -IF(ENABLE_ORC) - INCLUDE_DIRECTORIES(${ORC_INCLUDE_DIRS}) - LINK_DIRECTORIES(${ORC_LIBRARY_DIRS}) - ENABLE_LANGUAGE(C) - - SET(orcc_src ${CMAKE_CURRENT_SOURCE_DIR}/convert_orc.orc) - - GET_FILENAME_COMPONENT(orc_file_name_we ${orcc_src} NAME_WE) - SET(orcc_gen ${CMAKE_CURRENT_BINARY_DIR}/${orc_file_name_we}.c) - MESSAGE(STATUS "Orc found, enabling Orc support.") - ADD_CUSTOM_COMMAND( - COMMAND ${ORCC_EXECUTABLE} --implementation -o ${orcc_gen} ${orcc_src} - DEPENDS ${orcc_src} OUTPUT ${orcc_gen} - ) - LIBUHD_APPEND_SOURCES(${orcc_gen}) - LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/convert_with_orc.cpp - ) - LIBUHD_APPEND_LIBS(${ORC_LIBRARIES}) -ELSE(ENABLE_ORC) - MESSAGE(STATUS "Orc not found, disabling orc support.") -ENDIF(ENABLE_ORC) - -######################################################################## # Check for SSE2 SIMD headers ######################################################################## IF(CMAKE_COMPILER_IS_GNUCXX) diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp index 6c2ea9fec..6e73e9436 100644 --- a/host/lib/convert/convert_common.hpp +++ b/host/lib/convert/convert_common.hpp @@ -65,11 +65,10 @@ static const int PRIORITY_GENERAL = 0; static const int PRIORITY_EMPTY = -1; #ifdef __ARM_NEON__ -static const int PRIORITY_LIBORC = 3; -static const int PRIORITY_SIMD = 2; //neon conversions could be implemented better, orc wins +static const int PRIORITY_SIMD = 2; static const int PRIORITY_TABLE = 1; //tables require large cache, so they are slower on arm #else -static const int PRIORITY_LIBORC = 2; +// We used to have ORC, too, so SIMD is 3 static const int PRIORITY_SIMD = 3; static const int PRIORITY_TABLE = 1; #endif @@ -87,6 +86,7 @@ typedef float f32_t; typedef boost::int32_t s32_t; typedef boost::int16_t s16_t; typedef boost::int8_t s8_t; +typedef boost::uint8_t u8_t; typedef boost::uint32_t item32_t; diff --git a/host/lib/convert/convert_impl.cpp b/host/lib/convert/convert_impl.cpp index 329e94a4d..d90bb9c94 100644 --- a/host/lib/convert/convert_impl.cpp +++ b/host/lib/convert/convert_impl.cpp @@ -43,10 +43,10 @@ bool convert::operator==(const convert::id_type &lhs, const convert::id_type &rh std::string convert::id_type::to_pp_string(void) const{ return str(boost::format( "conversion ID\n" - " Input format: %s\n" - " Num inputs: %d\n" + " Input format: %s\n" + " Num inputs: %d\n" " Output format: %s\n" - " Num outputs: %d\n" + " Num outputs: %d\n" ) % this->input_format % this->num_inputs @@ -55,6 +55,15 @@ std::string convert::id_type::to_pp_string(void) const{ ); } +std::string convert::id_type::to_string(void) const{ + return str(boost::format("%s (%d) -> %s (%d)") + % this->input_format + % this->num_inputs + % this->output_format + % this->num_outputs + ); +} + /*********************************************************************** * Setup the table registry **********************************************************************/ @@ -92,7 +101,15 @@ convert::function_type convert::get_converter( //find a matching priority priority_type best_prio = -1; BOOST_FOREACH(priority_type prio_i, get_table()[id].keys()){ - if (prio_i == prio) return get_table()[id][prio]; + if (prio_i == prio) { + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + return get_table()[id][prio]; + } best_prio = std::max(best_prio, prio_i); } @@ -100,6 +117,13 @@ convert::function_type convert::get_converter( if (prio != -1) throw uhd::key_error( "Cannot find a conversion routine [with prio] for " + id.to_pp_string()); + //----------------------------------------------------------------// + UHD_LOGV(always) << "get_converter: For converter ID: " << id.to_pp_string() << std::endl + << "Using prio: " << best_prio << std::endl + << std::endl + ; + //----------------------------------------------------------------// + //otherwise, return best prio return get_table()[id][best_prio]; } @@ -148,6 +172,7 @@ UHD_STATIC_BLOCK(convert_register_item_sizes){ convert::register_bytes_per_item("s32", sizeof(boost::int32_t)); convert::register_bytes_per_item("s16", sizeof(boost::int16_t)); convert::register_bytes_per_item("s8", sizeof(boost::int8_t)); + convert::register_bytes_per_item("u8", sizeof(boost::uint8_t)); //register VITA types convert::register_bytes_per_item("item32", sizeof(boost::int32_t)); diff --git a/host/lib/convert/convert_orc.orc b/host/lib/convert/convert_orc.orc deleted file mode 100644 index ffb298f26..000000000 --- a/host/lib/convert/convert_orc.orc +++ /dev/null @@ -1,79 +0,0 @@ -.function _convert_fc32_1_to_item32_1_nswap_orc -.source 8 src -.dest 4 dst -.floatparam 4 scalar -.temp 8 scaled -.temp 8 converted -.temp 4 short -x2 mulf scaled, src, scalar -x2 convfl converted, scaled -x2 convlw short, converted -swapl short, short -x2 swapw dst, short - -.function _convert_fc32_1_to_item32_1_bswap_orc -.source 8 src -.dest 4 dst -.floatparam 4 scalar -.temp 8 scaled -.temp 8 converted -.temp 4 short -x2 mulf scaled, src, scalar -x2 convfl converted, scaled -x2 convlw short, converted -x2 swapw dst, short - -.function _convert_item32_1_to_fc32_1_nswap_orc -.source 4 src -.dest 8 dst -.floatparam 4 scalar -.temp 4 tmp1 -.temp 8 tmp2 -x2 swapw tmp1, src -swapl tmp1, tmp1 -x2 convswl tmp2, tmp1 -x2 convlf tmp2, tmp2 -x2 mulf dst, tmp2, scalar - -.function _convert_item32_1_to_fc32_1_bswap_orc -.source 4 src -.dest 8 dst -.floatparam 4 scalar -.temp 4 tmp1 -.temp 8 tmp2 -x2 swapw tmp1, src -x2 convswl tmp2, tmp1 -x2 convlf tmp2, tmp2 -x2 mulf dst, tmp2, scalar - -.function _convert_sc16_1_to_item32_1_nswap_orc -.source 4 src -.dest 4 dst -.temp 4 tmp -.floatparam 4 scalar -swapl tmp, src -x2 swapw dst, tmp - -.function _convert_item32_1_to_sc16_1_nswap_orc -.source 4 src -.dest 4 dst -.floatparam 4 scalar -.temp 4 tmp -x2 swapw tmp, src -swapl dst, tmp - -.function _convert_swap_byte_pairs_orc -.source 4 src -.dest 4 dst -swapl dst, src - -.function _convert_fc32_1_to_sc8_1_nswap_orc -.source 8 src -.dest 2 dst -.temp 8 tmp -.temp 4 tmp2 -.floatparam 4 scalar -x2 mulf tmp, src, scalar -x2 convfl tmp, tmp -x2 convlw tmp2, tmp -x2 convwb dst, tmp2 diff --git a/host/lib/convert/convert_with_orc.cpp b/host/lib/convert/convert_with_orc.cpp deleted file mode 100644 index 19755fa44..000000000 --- a/host/lib/convert/convert_with_orc.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright 2011-2013 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include "convert_common.hpp" -#include <uhd/utils/byteswap.hpp> - -using namespace uhd::convert; - -extern "C" { -extern void _convert_fc32_1_to_item32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_fc32_1_to_item32_1_bswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_fc32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_fc32_1_bswap_orc(void *, const void *, float, int); -extern void _convert_sc16_1_to_item32_1_nswap_orc(void *, const void *, float, int); -extern void _convert_item32_1_to_sc16_1_nswap_orc(void *, const void *, float, int); -extern void _convert_fc32_1_to_sc8_1_nswap_orc(void *, const void *, float, int); -extern void _convert_swap_byte_pairs_orc(void *, const void *, int); -} - -DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_item32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_LIBORC){ - _convert_item32_1_to_fc32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_LIBORC){ - _convert_item32_1_to_fc32_1_bswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(sc16, 1, sc16_item32_le, 1, PRIORITY_LIBORC){ - _convert_sc16_1_to_item32_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(sc16_item32_le, 1, sc16, 1, PRIORITY_LIBORC){ - _convert_item32_1_to_sc16_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(fc32, 1, sc8_item32_be, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); -} - -DECLARE_CONVERTER(fc32, 1, sc8_item32_le, 1, PRIORITY_LIBORC){ - _convert_fc32_1_to_sc8_1_nswap_orc(outputs[0], inputs[0], scale_factor, nsamps); - _convert_swap_byte_pairs_orc(outputs[0], outputs[0], (nsamps + 1)/2); -} diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py index b0790755a..4f9eeb747 100644 --- a/host/lib/convert/gen_convert_general.py +++ b/host/lib/convert/gen_convert_general.py @@ -17,92 +17,147 @@ # TMPL_HEADER = """ -#import time +<% + import time +%> /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#include "convert_common.hpp" -\#include <uhd/utils/byteswap.hpp> +#include "convert_common.hpp" +#include <uhd/utils/byteswap.hpp> using namespace uhd::convert; + + +// item32 -> item32: Just a memcpy. No scaling possible. +DECLARE_CONVERTER(item32, 1, item32, 1, PRIORITY_GENERAL) { + const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); + item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); + + memcpy(output, input, nsamps * sizeof(item32_t)); +} """ TMPL_CONV_GEN2_ITEM32 = """ -DECLARE_CONVERTER(item32, 1, sc16_item32_$(end), 1, PRIORITY_GENERAL){ +DECLARE_CONVERTER(item32, 1, sc16_item32_{end}, 1, PRIORITY_GENERAL) {{ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_wire)(input[i]); - } -} + for (size_t i = 0; i < nsamps; i++) {{ + output[i] = {to_wire}(input[i]); + }} +}} -DECLARE_CONVERTER(sc16_item32_$(end), 1, item32, 1, PRIORITY_GENERAL){ +DECLARE_CONVERTER(sc16_item32_{end}, 1, item32, 1, PRIORITY_GENERAL) {{ const item32_t *input = reinterpret_cast<const item32_t *>(inputs[0]); item32_t *output = reinterpret_cast<item32_t *>(outputs[0]); - for (size_t i = 0; i < nsamps; i++){ - output[i] = $(to_host)(input[i]); - } -} + for (size_t i = 0; i < nsamps; i++) {{ + output[i] = {to_host}(input[i]); + }} +}} +""" + +TMPL_CONV_U8 = """ +DECLARE_CONVERTER(u8, 1, u8_item32_{end}, 1, PRIORITY_GENERAL) {{ + const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); + boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); + + // 1) Copy all the 4-byte tuples + size_t n_words = nsamps / 4; + for (size_t i = 0; i < n_words; i++) {{ + output[i] = {to_wire}(input[i]); + }} + // 2) If nsamps was not a multiple of 4, copy the rest by hand + size_t bytes_left = nsamps % 4; + if (bytes_left) {{ + const u8_t *last_input_word = reinterpret_cast<const u8_t *>(&input[n_words]); + u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); + for (size_t k = 0; k < bytes_left; k++) {{ + last_output_word[k] = last_input_word[k]; + }} + output[n_words] = {to_wire}(output[n_words]); + }} +}} + +DECLARE_CONVERTER(u8_item32_{end}, 1, u8, 1, PRIORITY_GENERAL) {{ + const boost::uint32_t *input = reinterpret_cast<const boost::uint32_t *>(inputs[0]); + boost::uint32_t *output = reinterpret_cast<boost::uint32_t *>(outputs[0]); + + // 1) Copy all the 4-byte tuples + size_t n_words = nsamps / 4; + for (size_t i = 0; i < n_words; i++) {{ + output[i] = {to_host}(input[i]); + }} + // 2) If nsamps was not a multiple of 4, copy the rest by hand + size_t bytes_left = nsamps % 4; + if (bytes_left) {{ + boost::uint32_t last_input_word = {to_host}(input[n_words]); + const u8_t *last_input_word_ptr = reinterpret_cast<const u8_t *>(&last_input_word); + u8_t *last_output_word = reinterpret_cast<u8_t *>(&output[n_words]); + for (size_t k = 0; k < bytes_left; k++) {{ + last_output_word[k] = last_input_word_ptr[k]; + }} + }} +}} """ TMPL_CONV_USRP1_COMPLEX = """ -DECLARE_CONVERTER($(cpu_type), $(width), sc16_item16_usrp1, 1, PRIORITY_GENERAL){ - #for $w in range($width) - const $(cpu_type)_t *input$(w) = reinterpret_cast<const $(cpu_type)_t *>(inputs[$(w)]); - #end for +DECLARE_CONVERTER(${cpu_type}, ${width}, sc16_item16_usrp1, 1, PRIORITY_GENERAL){ + % for w in range(width): + const ${cpu_type}_t *input${w} = reinterpret_cast<const ${cpu_type}_t *>(inputs[${w}]); + % endfor boost::uint16_t *output = reinterpret_cast<boost::uint16_t *>(outputs[0]); for (size_t i = 0, j = 0; i < nsamps; i++){ - #for $w in range($width) - output[j++] = $(to_wire)(boost::uint16_t(boost::int16_t(input$(w)[i].real()$(do_scale)))); - output[j++] = $(to_wire)(boost::uint16_t(boost::int16_t(input$(w)[i].imag()$(do_scale)))); - #end for + % for w in range(width): + output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].real()${do_scale}))); + output[j++] = ${to_wire}(boost::uint16_t(boost::int16_t(input${w}[i].imag()${do_scale}))); + % endfor } } -DECLARE_CONVERTER(sc16_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +DECLARE_CONVERTER(sc16_item16_usrp1, 1, ${cpu_type}, ${width}, PRIORITY_GENERAL){ const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]); - #for $w in range($width) - $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]); - #end for + % for w in range(width): + ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]); + % endfor for (size_t i = 0, j = 0; i < nsamps; i++){ - #for $w in range($width) - output$(w)[i] = $(cpu_type)_t( - boost::int16_t($(to_host)(input[j+0]))$(do_scale), - boost::int16_t($(to_host)(input[j+1]))$(do_scale) + % for w in range(width): + output${w}[i] = ${cpu_type}_t( + boost::int16_t(${to_host}(input[j+0]))${do_scale}, + boost::int16_t(${to_host}(input[j+1]))${do_scale} ); j += 2; - #end for + % endfor } } -DECLARE_CONVERTER(sc8_item16_usrp1, 1, $(cpu_type), $(width), PRIORITY_GENERAL){ +DECLARE_CONVERTER(sc8_item16_usrp1, 1, ${cpu_type}, ${width}, PRIORITY_GENERAL){ const boost::uint16_t *input = reinterpret_cast<const boost::uint16_t *>(inputs[0]); - #for $w in range($width) - $(cpu_type)_t *output$(w) = reinterpret_cast<$(cpu_type)_t *>(outputs[$(w)]); - #end for + % for w in range(width): + ${cpu_type}_t *output${w} = reinterpret_cast<${cpu_type}_t *>(outputs[${w}]); + % endfor for (size_t i = 0, j = 0; i < nsamps; i++){ - #for $w in range($width) + % for w in range(width): { - const boost::uint16_t num = $(to_host)(input[j++]); - output$(w)[i] = $(cpu_type)_t( - boost::int8_t(num)$(do_scale), - boost::int8_t(num >> 8)$(do_scale) + const boost::uint16_t num = ${to_host}(input[j++]); + output${w}[i] = ${cpu_type}_t( + boost::int8_t(num)${do_scale}, + boost::int8_t(num >> 8)${do_scale} ); } - #end for + % endfor } } """ def parse_tmpl(_tmpl_text, **kwargs): - from Cheetah.Template import Template - return str(Template(_tmpl_text, kwargs)) + from mako.template import Template + return Template(_tmpl_text).render(**kwargs) if __name__ == '__main__': import sys, os @@ -114,12 +169,19 @@ if __name__ == '__main__': ('be', 'uhd::ntohx', 'uhd::htonx'), ('le', 'uhd::wtohx', 'uhd::htowx'), ): - output += parse_tmpl( - TMPL_CONV_GEN2_ITEM32, + output += TMPL_CONV_GEN2_ITEM32.format( end=end, to_host=to_host, to_wire=to_wire - ) + ) + #generate raw (u8) converters: + for end, to_host, to_wire in ( + ('be', 'uhd::ntohx', 'uhd::htonx'), + ('le', 'uhd::wtohx', 'uhd::htowx'), + ): + output += TMPL_CONV_U8.format( + end=end, to_host=to_host, to_wire=to_wire + ) - #generate complex converters for usrp1 format + #generate complex converters for usrp1 format (requires Cheetah) for width in 1, 2, 4: for cpu_type, do_scale in ( ('fc64', '*scale_factor'), diff --git a/host/lib/error_c.cpp b/host/lib/error_c.cpp new file mode 100644 index 000000000..3ce63a81d --- /dev/null +++ b/host/lib/error_c.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/error.h> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> + +#include <boost/thread/mutex.hpp> + +#include <cstring> + +#define MAP_TO_ERROR(exception_type, error_type) \ + if (dynamic_cast<const uhd::exception_type*>(e)) return error_type; + +uhd_error error_from_uhd_exception(const uhd::exception* e){ + MAP_TO_ERROR(index_error, UHD_ERROR_INDEX) + MAP_TO_ERROR(key_error, UHD_ERROR_KEY) + MAP_TO_ERROR(not_implemented_error, UHD_ERROR_NOT_IMPLEMENTED) + MAP_TO_ERROR(usb_error, UHD_ERROR_USB) + MAP_TO_ERROR(io_error, UHD_ERROR_IO) + MAP_TO_ERROR(os_error, UHD_ERROR_OS) + MAP_TO_ERROR(assertion_error, UHD_ERROR_ASSERTION) + MAP_TO_ERROR(lookup_error, UHD_ERROR_LOOKUP) + MAP_TO_ERROR(type_error, UHD_ERROR_TYPE) + MAP_TO_ERROR(value_error, UHD_ERROR_VALUE) + MAP_TO_ERROR(runtime_error, UHD_ERROR_RUNTIME) + MAP_TO_ERROR(environment_error, UHD_ERROR_ENVIRONMENT) + MAP_TO_ERROR(system_error, UHD_ERROR_SYSTEM) + + return UHD_ERROR_EXCEPT; +} + +// Store the error string in a single place in library +UHD_SINGLETON_FCN(std::string, _c_global_error_string) + +static boost::mutex _error_c_mutex; + +const std::string& get_c_global_error_string(){ + boost::mutex::scoped_lock lock(_error_c_mutex); + return _c_global_error_string(); +} + +void set_c_global_error_string( + const std::string &msg +){ + boost::mutex::scoped_lock lock(_error_c_mutex); + _c_global_error_string() = msg; +} + +uhd_error uhd_get_last_error( + char* error_out, + size_t strbuffer_len +){ + try{ + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, _c_global_error_string().c_str(), strbuffer_len); + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + return UHD_ERROR_NONE; +} diff --git a/host/lib/exception.cpp b/host/lib/exception.cpp index ea056bd3b..a9e36fd15 100644 --- a/host/lib/exception.cpp +++ b/host/lib/exception.cpp @@ -43,3 +43,8 @@ make_exception_impl("EnvironmentError", environment_error, exception) make_exception_impl("IOError", io_error, environment_error) make_exception_impl("OSError", os_error, environment_error) make_exception_impl("SystemError", system_error, exception) + +usb_error::usb_error(int code, const std::string &what): + runtime_error(str(boost::format("%s %d: %s") % "USBError" % code % what)), _code(code) {} +usb_error *usb_error::dynamic_clone(void) const{return new usb_error(*this);} \ +void usb_error::dynamic_throw(void) const{throw *this;} diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py index 24f5bf8be..5c0cfc109 100644..100755 --- a/host/lib/ic_reg_maps/common.py +++ b/host/lib/ic_reg_maps/common.py @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -18,105 +18,99 @@ import re import sys import math -from Cheetah.Template import Template +from mako.template import Template -COMMON_TMPL = """\ -#import time +COMMON_TMPL = """<% import time %>\ /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#ifndef INCLUDED_$(name.upper())_HPP -\#define INCLUDED_$(name.upper())_HPP +#ifndef INCLUDED_${name.upper()}_HPP +#define INCLUDED_${name.upper()}_HPP -\#include <uhd/config.hpp> -\#include <uhd/exception.hpp> -\#include <boost/cstdint.hpp> -\#include <set> +#include <uhd/config.hpp> +#include <uhd/exception.hpp> +#include <boost/cstdint.hpp> +#include <set> -class $(name)_t{ +class ${name}_t{ public: - #for $reg in $regs - #if $reg.get_enums() - enum $reg.get_type(){ - #for $i, $enum in enumerate($reg.get_enums()) - #set $end_comma = ',' if $i < len($reg.get_enums())-1 else '' - $(reg.get_name().upper())_$(enum[0].upper()) = $enum[1]$end_comma - #end for + % for reg in regs: + % if reg.get_enums(): + enum ${reg.get_type()}{ + % for i,enum in enumerate(reg.get_enums()): + ${reg.get_name().upper()}_${enum[0].upper()} = ${enum[1]}<% comma = ',' if i != (len(reg.get_enums())-1) else '' %>${comma} + % endfor }; - #end if - $reg.get_type() $reg.get_name(); - #end for + % endif + ${reg.get_type()} ${reg.get_name()}; + % endfor - $(name)_t(void){ + ${name}_t(void){ _state = NULL; - #for $reg in $regs - $reg.get_name() = $reg.get_default(); - #end for + % for reg in regs: + ${reg.get_name()} = ${reg.get_default()}; + % endfor } - ~$(name)_t(void){ + ~${name}_t(void){ delete _state; } - $body + ${body} void save_state(void){ - if (_state == NULL) _state = new $(name)_t(); - #for $reg in $regs - _state->$reg.get_name() = this->$reg.get_name(); - #end for + if (_state == NULL) _state = new ${name}_t(); + % for reg in regs: + _state->${reg.get_name()} = this->${reg.get_name()}; + % endfor } template<typename T> std::set<T> get_changed_addrs(void){ if (_state == NULL) throw uhd::runtime_error("no saved state"); //check each register for changes std::set<T> addrs; - #for $reg in $regs - if(_state->$reg.get_name() != this->$reg.get_name()){ - addrs.insert($reg.get_addr()); + % for reg in regs: + if(_state->${reg.get_name()} != this->${reg.get_name()}){ + addrs.insert(${reg.get_addr()}); } - #end for + % endfor return addrs; } - #for $mreg in $mregs - $mreg.get_type() get_$(mreg.get_name())(void){ - return - #set $shift = 0 - #for $reg in $mreg.get_regs() - ($(mreg.get_type())($reg.get_name() & $reg.get_mask()) << $shift) | - #set $shift = $shift + $reg.get_bit_width() - #end for + % for mreg in mregs: + ${mreg.get_type()} get_${mreg.get_name()}(void){ + return <% shift = 0 %> + % for reg in mreg.get_regs(): + (${mreg.get_type()}(${reg.get_name()} & ${reg.get_mask()}) << ${shift}) |<% shift = shift + reg.get_bit_width() %> + % endfor 0; } - void set_$(mreg.get_name())($mreg.get_type() reg){ - #set $shift = 0 - #for $reg in $mreg.get_regs() - $reg.get_name() = (reg >> $shift) & $reg.get_mask(); - #set $shift = $shift + $reg.get_bit_width() - #end for + void set_${mreg.get_name()}(${mreg.get_type()} reg){<% shift = 0 %> + % for reg in mreg.get_regs(): + ${reg.get_name()} = (reg >> ${shift}) & ${reg.get_mask()};<% shift = shift + reg.get_bit_width() %> + % endfor } - #end for + % endfor private: - $(name)_t *_state; + ${name}_t *_state; }; -\#endif /* INCLUDED_$(name.upper())_HPP */ +#endif /* INCLUDED_${name.upper()}_HPP */ """ def parse_tmpl(_tmpl_text, **kwargs): - return str(Template(_tmpl_text, kwargs)) + return Template(_tmpl_text).render(**kwargs) def to_num(arg): return int(eval(arg)) class reg: def __init__(self, reg_des): try: self.parse(reg_des) - except Exception, e: - raise Exception, 'Error parsing register description: "%s"\nWhat: %s'%(reg_des, e) + except Exception as e: + raise Exception('Error parsing register description: "%s"\nWhat: %s'%(reg_des, e)) def parse(self, reg_des): x = re.match('^(\w*)\s*(\w*)\[(.*)\]\s*(\w*)\s*(.*)$', reg_des) @@ -133,7 +127,8 @@ class reg: self._enums = list() if enums: enum_val = 0 - for enum_str in map(str.strip, enums.split(',')): + for enum_str_unstripped in enums.split(','): + enum_str = enum_str_unstripped.strip() if '=' in enum_str: enum_name, enum_val = enum_str.split('=') enum_val = to_num(enum_val) @@ -146,7 +141,7 @@ class reg: def get_name(self): return self._name def get_default(self): for key, val in self.get_enums(): - if val == self._default: return str.upper('%s_%s'%(self.get_name(), key)) + if val == self._default: return ('%s_%s'%(self.get_name(), key)).upper() return self._default def get_type(self): if self.get_enums(): return '%s_t'%self.get_name() @@ -158,14 +153,14 @@ class reg: class mreg: def __init__(self, mreg_des, regs): try: self.parse(mreg_des, regs) - except Exception, e: - raise Exception, 'Error parsing meta register description: "%s"\nWhat: %s'%(mreg_des, e) + except Exception as e: + raise Exception('Error parsing meta register description: "%s"\nWhat: %s'%(mreg_des, e)) def parse(self, mreg_des, regs): x = re.match('^~(\w*)\s+(.*)\s*$', mreg_des) self._name, reg_names = x.groups() regs_dict = dict([(reg.get_name(), reg) for reg in regs]) - self._regs = [regs_dict[reg_name] for reg_name in map(str.strip, reg_names.split(','))] + self._regs = [regs_dict[reg_name.strip()] for reg_name in reg_names.split(',')] def get_name(self): return self._name def get_regs(self): return self._regs diff --git a/host/lib/ic_reg_maps/gen_ad5623_regs.py b/host/lib/ic_reg_maps/gen_ad5623_regs.py index e653921ba..8b70a9f0a 100755 --- a/host/lib/ic_reg_maps/gen_ad5623_regs.py +++ b/host/lib/ic_reg_maps/gen_ad5623_regs.py @@ -32,9 +32,9 @@ cmd 0[19:21] 0 wr_input_n, up_dac_n, wr_input_n_up_a BODY_TMPL="""\ boost::uint32_t get_reg(void){ boost::uint32_t reg = 0; - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor return reg; } """ diff --git a/host/lib/ic_reg_maps/gen_ad7922_regs.py b/host/lib/ic_reg_maps/gen_ad7922_regs.py index 5cec1924a..c77991182 100755 --- a/host/lib/ic_reg_maps/gen_ad7922_regs.py +++ b/host/lib/ic_reg_maps/gen_ad7922_regs.py @@ -32,16 +32,16 @@ chn 0[13] 0 BODY_TMPL="""\ boost::uint16_t get_reg(void){ boost::uint16_t reg = 0; - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor return reg; } void set_reg(boost::uint16_t reg){ - #for $reg in filter(lambda r: r.get_addr() == 0, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for reg in filter(lambda r: r.get_addr() == 0, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor } """ diff --git a/host/lib/ic_reg_maps/gen_ad9510_regs.py b/host/lib/ic_reg_maps/gen_ad9510_regs.py index 6c1e612cc..9f194b5c9 100755 --- a/host/lib/ic_reg_maps/gen_ad9510_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9510_regs.py @@ -54,25 +54,25 @@ lock_detect_disable 0xD[6] 0 enb, dis ######################################################################## ## fine delay adjust ######################################################################## -#for $i, $o in ((5, 0), (6, 4)) -delay_control_out$i $hex(0x34+$o)[0] 0 -ramp_current_out$i $hex(0x35+$o)[0:2] 0 200ua, 400ua, 600ua, 800ua, 1000ua, 1200ua, 1400ua, 1600ua -ramp_capacitor_out$i $hex(0x35+$o)[3:5] 0 4caps=0, 3caps=1, 2caps=3, 1cap=7 -delay_fine_adjust_out$i $hex(0x36+$o)[1:5] 0 -#end for +% for i, o in ((5, 0), (6, 4)): +delay_control_out${i} ${hex(0x34+o)}[0] 0 +ramp_current_out${i} ${hex(0x35+o)}[0:2] 0 200ua, 400ua, 600ua, 800ua, 1000ua, 1200ua, 1400ua, 1600ua +ramp_capacitor_out${i} ${hex(0x35+o)}[3:5] 0 4caps=0, 3caps=1, 2caps=3, 1cap=7 +delay_fine_adjust_out${i} ${hex(0x36+o)}[1:5] 0 +% endfor ######################################################################## ## outputs ######################################################################## -#for $i, $o in ((0, 0), (1, 1), (2, 2), (3, 3)) -power_down_lvpecl_out$i $hex(0x3C+$o)[0:1] 0 normal, test, safe_pd, total_pd -output_level_lvpecl_out$i $hex(0x3C+$o)[2:3] 2 500mv, 340mv, 810mv, 660mv -#end for -#for $i, $o in ((4, 0), (5, 1), (6, 2), (7, 3)) -power_down_lvds_cmos_out$i $hex(0x40+$o)[0] 0 -output_level_lvds_out$i $hex(0x40+$o)[1:2] 1 1_75ma, 3_5ma, 5_25ma, 7ma -lvds_cmos_select_out$i $hex(0x40+$o)[3] 1 lvds, cmos -inverted_cmos_driver_out$i $hex(0x40+$o)[4] 0 dis, enb -#end for +% for i, o in ((0, 0), (1, 1), (2, 2), (3, 3)): +power_down_lvpecl_out${i} ${hex(0x3C+o)}[0:1] 0 normal, test, safe_pd, total_pd +output_level_lvpecl_out${i} ${hex(0x3C+o)}[2:3] 2 500mv, 340mv, 810mv, 660mv +% endfor +% for i, o in ((4, 0), (5, 1), (6, 2), (7, 3)): +power_down_lvds_cmos_out${i} ${hex(0x40+o)}[0] 0 +output_level_lvds_out${i} ${hex(0x40+o)}[1:2] 1 1_75ma, 3_5ma, 5_25ma, 7ma +lvds_cmos_select_out${i} ${hex(0x40+o)}[3] 1 lvds, cmos +inverted_cmos_driver_out${i} ${hex(0x40+o)}[4] 0 dis, enb +% endfor clock_select 0x45[0] 1 clk2_drives, clk1_drives clk1_power_down 0x45[1] 0 clk2_power_down 0x45[2] 0 @@ -82,15 +82,15 @@ all_clock_inputs_pd 0x45[5] 0 ######################################################################## ## dividers ######################################################################## -#for $i, $o in ((0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14)) -divider_high_cycles_out$i $hex(0x48+$o)[0:3] 0 -divider_low_cycles_out$i $hex(0x48+$o)[4:7] 0 -phase_offset_out$i $hex(0x49+$o)[0:3] 0 -start_out$i $hex(0x49+$o)[4] 0 -force_out$i $hex(0x49+$o)[5] 0 -nosync_out$i $hex(0x49+$o)[6] 0 -bypass_divider_out$i $hex(0x49+$o)[7] 0 -#end for +% for i, o in ((0, 0), (1, 2), (2, 4), (3, 6), (4, 8), (5, 10), (6, 12), (7, 14)): +divider_high_cycles_out${i} ${hex(0x48+o)}[0:3] 0 +divider_low_cycles_out${i} ${hex(0x48+o)}[4:7] 0 +phase_offset_out${i} ${hex(0x49+o)}[0:3] 0 +start_out${i} ${hex(0x49+o)}[4] 0 +force_out${i} ${hex(0x49+o)}[5] 0 +nosync_out${i} ${hex(0x49+o)}[6] 0 +bypass_divider_out${i} ${hex(0x49+o)}[7] 0 +% endfor ######################################################################## ## function ######################################################################## @@ -110,13 +110,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint16_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py index 1512da811..cc906b76c 100755 --- a/host/lib/ic_reg_maps/gen_ad9522_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -88,39 +88,38 @@ ref2_freq_gt_thresh 0x01F[2] 0 ref1_freq_gt_thresh 0x01F[1] 0 digital_lock_detect 0x01F[0] 0 ######################################################################## -#for $i in range(12) -#set $addr = ($i + 0x0F0) -out$(i)_format $(addr)[7] 0 lvds, cmos -out$(i)_cmos_configuration $(addr)[6:5] 3 off, a_on, b_on, ab_on -out$(i)_polarity $(addr)[4:3] 0 lvds_a_non_b_inv=0, lvds_a_inv_b_non=1, cmos_ab_non=0, cmos_ab_inv=1, cmos_a_non_b_inv=2, cmos_a_inv_b_non=3 -out$(i)_lvds_diff_voltage $(addr)[2:1] 1 1_75ma, 3_5ma, 5_25ma, 7_0ma -out$(i)_lvds_power_down $(addr)[0] 0 -#end for +% for i in range(12): +<% addr = (i + 0x0F0) %>\ +out${i}_format ${addr}[7] 0 lvds, cmos +out${i}_cmos_configuration ${addr}[6:5] 3 off, a_on, b_on, ab_on +out${i}_polarity ${addr}[4:3] 0 lvds_a_non_b_inv=0, lvds_a_inv_b_non=1, cmos_ab_non=0, cmos_ab_inv=1, cmos_a_non_b_inv=2, cmos_a_inv_b_non=3 +out${i}_lvds_diff_voltage ${addr}[2:1] 1 1_75ma, 3_5ma, 5_25ma, 7_0ma +out${i}_lvds_power_down ${addr}[0] 0 +% endfor ######################################################################## -#for $i in reversed(range(8)) -csdld_en_out_$i 0x0FC[$i] 0 ignore, async -#end for +% for i in reversed(range(8)): +csdld_en_out_${i} 0x0FC[${i}] 0 ignore, async +% endfor ######################################################################## -#for $i in reversed(range(4)) -csdld_en_out_$(8 + $i) 0x0FD[$i] 0 ignore, async -#end for +% for i in reversed(range(4)): +csdld_en_out_${8 + i} 0x0FD[${i}] 0 ignore, async +% endfor ######################################################################## -#set $default_val = 0x7 -#for $i in range(4) -#set $addr0 = hex($i*3 + 0x190) -#set $addr1 = hex($i*3 + 0x191) -#set $addr2 = hex($i*3 + 0x192) -divider$(i)_low_cycles $(addr0)[7:4] $default_val -divider$(i)_high_cycles $(addr0)[3:0] $default_val -divider$(i)_bypass $(addr1)[7] 0 -divider$(i)_ignore_sync $(addr1)[6] 0 -divider$(i)_force_high $(addr1)[5] 0 -divider$(i)_start_high $(addr1)[4] 0 -divider$(i)_phase_offset $(addr1)[3:0] 0 -channel$(i)_power_down $(addr2)[2] 0 -disable_divider$(i)_ddc $(addr2)[0] 0 -#set $default_val /= 2 -#end for +% for i in range(4): +<% default_val = int(0x7 / (2**i)) %>\ +<% addr0 = hex(i*3 + 0x190) %>\ +<% addr1 = hex(i*3 + 0x191) %>\ +<% addr2 = hex(i*3 + 0x192) %>\ +divider${i}_low_cycles ${addr0}[7:4] ${default_val} +divider${i}_high_cycles ${addr0}[3:0] ${default_val} +divider${i}_bypass ${addr1}[7] 0 +divider${i}_ignore_sync ${addr1}[6] 0 +divider${i}_force_high ${addr1}[5] 0 +divider${i}_start_high ${addr1}[4] 0 +divider${i}_phase_offset ${addr1}[3:0] 0 +channel${i}_power_down ${addr2}[2] 0 +disable_divider${i}_ddc ${addr2}[0] 0 +% endfor ######################################################################## vco_divider 0x1E0[2:0] 2 div2, div3, div4, div5, div6, static, div1 power_down_clock_input_sel 0x1E1[4] 0 @@ -145,13 +144,13 @@ BODY_TMPL="""\ boost::uint32_t get_reg(boost::uint16_t addr){ boost::uint32_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } if (addr == 0){ //mirror 4 bits in register 0 reg |= ((reg >> 7) & 0x1) << 0; @@ -164,13 +163,13 @@ boost::uint32_t get_reg(boost::uint16_t addr){ void set_reg(boost::uint16_t addr, boost::uint32_t reg){ switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor break; - #end for + % endfor } } diff --git a/host/lib/ic_reg_maps/gen_ad9777_regs.py b/host/lib/ic_reg_maps/gen_ad9777_regs.py index 47b61cf44..514283409 100755 --- a/host/lib/ic_reg_maps/gen_ad9777_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9777_regs.py @@ -91,13 +91,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ad9862_regs.py b/host/lib/ic_reg_maps/gen_ad9862_regs.py index 00340224c..022d97c16 100755 --- a/host/lib/ic_reg_maps/gen_ad9862_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9862_regs.py @@ -41,10 +41,10 @@ all_rx_pd 1[0] 0 ######################################################################## ## Rx A and B ######################################################################## -#for $x, $i in (('a', 2), ('b', 3)) -byp_buffer_$x $(i)[7] 0 -rx_pga_$x $(i)[0:4] 0 -#end for +% for x, i in (('a', 2), ('b', 3)): +byp_buffer_${x} ${i}[7] 0 +rx_pga_${x} ${i}[0:4] 0 +% endfor ######################################################################## ## Rx Misc ######################################################################## @@ -76,13 +76,13 @@ tx_analog_pd 8[0:2] 0 none=0, txb=4, txa=2, both=7 ######################################################################## ## Tx Offset and Gain ######################################################################## -#for $x, $i, $j, $k in (('a', 10, 11, 14), ('b', 12, 13, 15)) -dac_$(x)_offset_1_0 $(i)[6:7] 0 -dac_$(x)_offset_dir $(i)[0] 0 neg_diff, pos_dif -dac_$(x)_offset_9_2 $(j)[0:7] 0 -dac_$(x)_coarse_gain $(k)[6:7] 0 -dac_$(x)_fine_gain $(k)[0:5] 0 -#end for +% for x, i, j, k in (('a', 10, 11, 14), ('b', 12, 13, 15)): +dac_${x}_offset_1_0 ${i}[6:7] 0 +dac_${x}_offset_dir ${i}[0] 0 neg_diff, pos_dif +dac_${x}_offset_9_2 ${j}[0:7] 0 +dac_${x}_coarse_gain ${k}[6:7] 0 +dac_${x}_fine_gain ${k}[0:5] 0 +% endfor tx_pga_gain 16[0:7] 0 ######################################################################## ## Tx Misc @@ -139,20 +139,20 @@ dis1 25[0] 0 enb, dis ######################################################################## ## Aux ADC ######################################################################## -#for $x, $i in (('a2', 26), ('a1', 28), ('b2', 30), ('b1', 32)) -aux_adc_$(x)_1_0 $(i)[6:7] 0 -aux_adc_$(x)_9_2 $int(1+$i)[0:7] 0 -#end for +% for x, i in (('a2', 26), ('a1', 28), ('b2', 30), ('b1', 32)): +aux_adc_${x}_1_0 ${i}[6:7] 0 +aux_adc_${x}_9_2 ${int(1+i)}[0:7] 0 +% endfor ######################################################################## ## Aux ADC Control ######################################################################## aux_spi 34[7] 0 dis, enb sel_bnota 34[6] 0 adc_a, adc_b -#for $x, $i in (('b', 5), ('a', 2)) -refsel_$(x) 34[$i] 0 external, internal -select_$(x) 34[$int($i-1)] 0 aux_adc2, aux_adc1 -start_$(x) 34[$int($i-2)] 0 -#end for +% for x, i in (('b', 5), ('a', 2)): +refsel_${x} 34[${i}] 0 external, internal +select_${x} 34[${int(i-1)}] 0 aux_adc2, aux_adc1 +start_${x} 34[${int(i-2)}] 0 +% endfor ######################################################################## ## Aux ADC Clock ######################################################################## @@ -160,9 +160,9 @@ clk_4 35[0] 0 1_2, 1_4 ######################################################################## ## Aux DAC ######################################################################## -#for $x, $i in (('a', 36), ('b', 37), ('c', 38)) -aux_dac_$x $(i)[0:7] 0 -#end for +% for x, i in (('a', 36), ('b', 37), ('c', 38)): +aux_dac_${x} ${i}[0:7] 0 +% endfor ######################################################################## ## Aux DAC Update ######################################################################## @@ -205,26 +205,26 @@ BODY_TMPL=""" boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in range(0, 63+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint16_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(0, 63+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint16_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } void set_reg(boost::uint8_t addr, boost::uint16_t reg){ switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor break; - #end for + % endfor } } diff --git a/host/lib/ic_reg_maps/gen_adf4350_regs.py b/host/lib/ic_reg_maps/gen_adf4350_regs.py index fce2f569b..644654dee 100755 --- a/host/lib/ic_reg_maps/gen_adf4350_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4350_regs.py @@ -43,8 +43,8 @@ power_down 2[5] 0 disabled, enabled pd_polarity 2[6] 1 negative, positive ldp 2[7] 0 10ns, 6ns ldf 2[8] 0 frac_n, int_n -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) -charge_pump_current 2[9:12] 5 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) %>\ +charge_pump_current 2[9:12] 5 ${current_setting_enums} double_buffer 2[13] 0 disabled, enabled r_counter_10_bit 2[14:23] 0 reference_divide_by_2 2[24] 1 disabled, enabled @@ -101,13 +101,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py index 4b0ef788c..6699e5137 100755 --- a/host/lib/ic_reg_maps/gen_adf4351_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py @@ -44,8 +44,8 @@ power_down 2[5] 0 disabled, enabled pd_polarity 2[6] 1 negative, positive ldp 2[7] 0 10ns, 6ns ldf 2[8] 0 frac_n, int_n -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) -charge_pump_current 2[9:12] 5 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) %>\ +charge_pump_current 2[9:12] 5 ${current_setting_enums} double_buffer 2[13] 0 disabled, enabled r_counter_10_bit 2[14:23] 0 reference_divide_by_2 2[24] 1 disabled, enabled @@ -105,13 +105,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_adf4360_regs.py b/host/lib/ic_reg_maps/gen_adf4360_regs.py index 3fd8707a7..921f014ff 100755 --- a/host/lib/ic_reg_maps/gen_adf4360_regs.py +++ b/host/lib/ic_reg_maps/gen_adf4360_regs.py @@ -32,9 +32,9 @@ charge_pump_output 0[9] 0 normal, 3state cp_gain_0 0[10] 0 set1, set2 mute_till_ld 0[11] 0 dis, enb output_power_level 0[12:13] 0 3_5ma, 5_0ma, 7_5ma, 11_0ma -#set $current_setting_enums = ', '.join(map(lambda x: x+"ma", "0_31 0_62 0_93 1_25 1_56 1_87 2_18 2_50".split())) -current_setting1 0[14:16] 0 $current_setting_enums -current_setting2 0[17:19] 0 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: x+"ma", "0_31 0_62 0_93 1_25 1_56 1_87 2_18 2_50".split())) %>\ +current_setting1 0[14:16] 0 ${current_setting_enums} +current_setting2 0[17:19] 0 ${current_setting_enums} power_down 0[20:21] 0 normal_op=0, async_pd=1, sync_pd=3 prescaler_value 0[22:23] 0 8_9, 16_17, 32_33 ######################################################################## @@ -68,13 +68,13 @@ enum addr_t{ boost::uint32_t get_reg(addr_t addr){ boost::uint32_t reg = addr & 0x3; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ads62p44_regs.py b/host/lib/ic_reg_maps/gen_ads62p44_regs.py index f0a84d940..df5c0c66c 100755 --- a/host/lib/ic_reg_maps/gen_ads62p44_regs.py +++ b/host/lib/ic_reg_maps/gen_ads62p44_regs.py @@ -95,13 +95,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_ads62p48_regs.py b/host/lib/ic_reg_maps/gen_ads62p48_regs.py index c38ce8ff1..fa5668d4f 100644..100755 --- a/host/lib/ic_reg_maps/gen_ads62p48_regs.py +++ b/host/lib/ic_reg_maps/gen_ads62p48_regs.py @@ -55,13 +55,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_lmk04816_regs.py b/host/lib/ic_reg_maps/gen_lmk04816_regs.py index e89a82671..d432ac706 100644..100755 --- a/host/lib/ic_reg_maps/gen_lmk04816_regs.py +++ b/host/lib/ic_reg_maps/gen_lmk04816_regs.py @@ -1,4 +1,4 @@ -#Copyright 2010 Ettus Research LLC +#Copyright 2010,2015 Ettus Research LLC # # 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 @@ -26,7 +26,7 @@ address0 0[0:4] 0 CLKout0_1_DIV 0[5:15] 25 CLKout0_1_HS 0[16] 0 RESET 0[17] 0 no_reset, reset -CLKout0_1_DDLY 0[18:27] 0 five +CLKout0_1_DDLY 0[18:27] 0 CLKout0_ADLY_SEL 0[28] 0 d_pd, d_ev_x, d_odd_y, d_both CLKout1_ADLY_SEL 0[29] 0 d_pd, d_ev_x, d_odd_y, d_both Required_0 0[30] 0 @@ -94,15 +94,15 @@ CLKout10_11_PD 5[31] 1 normal, power_down ######################################################################## ## address 6 ######################################################################## -#set $CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" +<% CLKoutX_TYPE_ENUMS = "p_down=0, LVDS=1, LVPECL_700mVpp=2, LVPECL_1200mVpp=3, LVPECL_1600mVpp=4, LVPECL_200mVpp=5, LVCMOS=6, LVCMOS_IN=7, LVCMOS_NN=8, LVCMOS_II=9, LVCMOS_LN=10, LVCMOS_LI=11, LVCMOS_NL=12, LVCMOS_IL=13, LVCMOS_LL=1" %>\ address6 6[0:4] 6 CLKout0_1_ADLY 6[5:9] 0 Required_6_10 6[10] 0 CLKout2_3_ADLY 6[11:15] 0 -CLKout0_TYPE 6[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout1_TYPE 6[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout2_TYPE 6[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout3_TYPE 6[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout0_TYPE 6[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout1_TYPE 6[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout2_TYPE 6[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout3_TYPE 6[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 7 ######################################################################## @@ -110,21 +110,21 @@ address7 7[0:4] 7 CLKout4_5_ADLY 7[5:9] 0 Required_7_10 7[10] 0 CLKout6_7_ADLY 7[11:15] 0 -CLKout4_TYPE 7[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout5_TYPE 7[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout6_TYPE 7[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout7_TYPE 7[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout4_TYPE 7[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout5_TYPE 7[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout6_TYPE 7[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout7_TYPE 7[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 8 ######################################################################## address8 8[0:4] 8 CLKout8_9_ADLY 8[5:9] 0 Required_8_10 8[10] 0 -CLKout10_11_ADLY 8[11:15] 0 $(CLKoutX_TYPE_ENUMS) -CLKout8_TYPE 8[16:19] 0 $(CLKoutX_TYPE_ENUMS) -CLKout9_TYPE 8[20:23] 0 $(CLKoutX_TYPE_ENUMS) -CLKout10_TYPE 8[24:27] 0 $(CLKoutX_TYPE_ENUMS) -CLKout11_TYPE 8[28:31] 0 $(CLKoutX_TYPE_ENUMS) +CLKout10_11_ADLY 8[11:15] 0 ${CLKoutX_TYPE_ENUMS} +CLKout8_TYPE 8[16:19] 0 ${CLKoutX_TYPE_ENUMS} +CLKout9_TYPE 8[20:23] 0 ${CLKoutX_TYPE_ENUMS} +CLKout10_TYPE 8[24:27] 0 ${CLKoutX_TYPE_ENUMS} +CLKout11_TYPE 8[28:31] 0 ${CLKoutX_TYPE_ENUMS} ######################################################################## ## address 9 ######################################################################## @@ -378,22 +378,18 @@ BODY_TMPL = """\ boost::uint32_t get_reg(int addr){ boost::uint32_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } """ - - - - if __name__ == '__main__': import common; common.generate( name='lmk04816_regs', @@ -402,6 +398,3 @@ if __name__ == '__main__': file=__file__, ) - - - diff --git a/host/lib/ic_reg_maps/gen_max2112_regs.py b/host/lib/ic_reg_maps/gen_max2112_regs.py index c2fc4e3e2..be760ec2e 100755 --- a/host/lib/ic_reg_maps/gen_max2112_regs.py +++ b/host/lib/ic_reg_maps/gen_max2112_regs.py @@ -53,13 +53,14 @@ f_divider_lsb 4[0:7] 0x84 ######################################################################## ## XTAL-Divider R-Divider (5) Write ######################################################################## -#set $xtal_divider_names = ', '.join(map(lambda x: 'div' + str(x), range(1,9))) -xtal_divider 5[5:7] 0 $xtal_divider_names +<% xtal_divider_names = ', '.join(map(lambda x: 'div' + str(x), range(1,9))) %>\ +xtal_divider 5[5:7] 0 ${xtal_divider_names} r_divider 5[0:4] 1 ######################################################################## ## PLL (6) Write ######################################################################## -d24 6[7] 1 div2, div4 ## div2 for LO <= 1125M, div4 > 1125M +## div2 for LO <= 1125M, div4 > 1125M +d24 6[7] 1 div2, div4 cps 6[6] 1 i_cp_from_icp, i_cp_from_vas icp 6[5] 0 i_cp_600ua, i_cp_1200ua ##reserved 6[0:4] 0 @@ -73,7 +74,8 @@ ade 7[0] 1 disabled, enabled ######################################################################## ## LPF (8) Write ######################################################################## -lp 8[0:7] 0x4B ## map(lambda x: "%0.2f"%((4e6 + (x - 12) * 290e3)/1e6), range(255)) in MHz +## map(lambda x: "%0.2f"%((4e6 + (x - 12) * 290e3)/1e6), range(255)) in MHz +lp 8[0:7] 0x4B ######################################################################## ## Control (9) Write ######################################################################## @@ -81,7 +83,8 @@ stby 9[7] 0 normal, disable_sig_and_synth ##reserved 9[6] 0 pwdn 9[5] 0 normal, invalid ##reserved 9[4] 0 -bbg 9[0:3] 0 ## Baseband Gain in dB +## Baseband Gain in dB +bbg 9[0:3] 0 ######################################################################## ## Shutdown (0xA) Write ######################################################################## @@ -118,7 +121,8 @@ ld 0xC[4] 0 unlocked, locked ######################################################################## ## Status Byte-2 (0xD) Read ######################################################################## -vcosbr 0xD[3:7] 0 ## vco band readback +## vco band readback +vcosbr 0xD[3:7] 0 adc 0xD[0:2] 0 ool0, lock0, vaslock0, vaslock1, vaslock2, vaslock3, lock1, ool1 """ @@ -129,41 +133,30 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_t reg){ switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor break; - #end for + % endfor } } """ -SPLIT_REGS_HELPER_TMPL="""\ -#for $divname in ['n','f'] -void set_$(divname)_divider(boost::uint32_t $divname){ - #for $regname in sorted(map(lambda r: r.get_name(), filter(lambda r: r.get_name().find(divname + '_divider') == 0, $regs))) - #end for -} -#end for -""" - #$regname = boost::uint8_t($divname & $regs[regname].get_mask()); - #$divname = boost::uint32_t($divname >> $regs[regname].get_shift()); - if __name__ == '__main__': import common; common.generate( name='max2112_write_regs', diff --git a/host/lib/ic_reg_maps/gen_max2118_regs.py b/host/lib/ic_reg_maps/gen_max2118_regs.py index 506fbaec8..01d7615de 100755 --- a/host/lib/ic_reg_maps/gen_max2118_regs.py +++ b/host/lib/ic_reg_maps/gen_max2118_regs.py @@ -38,28 +38,31 @@ n_divider_lsb 1[0:7] 0xB6 ######################################################################## ## R, Charge Pump, and VCO (2) Write ######################################################################## -#set $r_divider_names = ', '.join(map(lambda x: 'div' + str(2**(x+1)), range(0,8))) -r_divider 2[5:7] 1 $r_divider_names -#set $cp_current_bias = ', '.join(map(lambda x: 'i_cp_%dua'%(50*2**x), range(0,4))) -cp_current 2[3:4] 3 $cp_current_bias +<% r_divider_names = ', '.join(map(lambda x: 'div' + str(2**(x+1)), range(0,8))) %>\ +r_divider 2[5:7] 1 ${r_divider_names} +<% cp_current_bias = ', '.join(map(lambda x: 'i_cp_%dua'%(50*2**x), range(0,4))) %>\ +cp_current 2[3:4] 3 ${cp_current_bias} osc_band 2[0:2] 5 ######################################################################## ## I/Q Filter DAC (3) Write ######################################################################## ##unused 3[7] 0 -f_dac 3[0:6] 0x7F ## filter tuning dac, depends on m +## filter tuning dac, depends on m +f_dac 3[0:6] 0x7F ######################################################################## ## LPF Divider DAC (4) Write ######################################################################## adl_vco_adc_latch 4[7] 0 disabled, enabled ade_vco_ade_read 4[6] 0 disabled, enabled dl_output_drive 4[5] 0 iq_590m_vpp, iq_1_vpp -m_divider 4[0:4] 2 ## filter tuning counter +## filter tuning counter +m_divider 4[0:4] 2 ######################################################################## ## GC2 and Diag (5) Write ######################################################################## diag 5[5:7] 0 normal, cp_i_source, cp_i_sink, cp_high_z, unused, n_and_filt, r_and_gc2, m_div -gc2 5[0:4] 0x1F ## Step Size: 0-1: 0dB, 2-22: 1dB, 23-31: 0.5dB +## Step Size: 0-1: 0dB, 2-22: 1dB, 23-31: 0.5dB +gc2 5[0:4] 0x1F """ ######################################################################## @@ -71,11 +74,13 @@ READ_REGS_TMPL="""\ ## Status (0) Read ######################################################################## pwr 0[6] 0 not_reset, reset -adc 0[2:4] 0 ## VCO tuning voltage, Lock Status +## VCO tuning voltage, Lock Status +adc 0[2:4] 0 ######################################################################## ## I/Q Filter DAC (1) Read ######################################################################## -filter_dac 1[0:6] 0 ## I/Q Filter tuning DAC, current +## I/Q Filter tuning DAC, current +filter_dac 1[0:6] 0 """ ######################################################################## @@ -85,26 +90,26 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_t reg){ switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor break; - #end for + % endfor } } """ diff --git a/host/lib/ic_reg_maps/gen_max2829_regs.py b/host/lib/ic_reg_maps/gen_max2829_regs.py index 383131c18..dbcb68ec9 100755 --- a/host/lib/ic_reg_maps/gen_max2829_regs.py +++ b/host/lib/ic_reg_maps/gen_max2829_regs.py @@ -112,13 +112,13 @@ BODY_TMPL="""\ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint16_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint16_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint16_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return (boost::uint32_t(reg) << 4) | (addr & 0xf); } diff --git a/host/lib/ic_reg_maps/gen_max2870_regs.py b/host/lib/ic_reg_maps/gen_max2870_regs.py index f26c27281..af4e3c786 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2870_regs.py +++ b/host/lib/ic_reg_maps/gen_max2870_regs.py @@ -28,8 +28,10 @@ REGS_TMPL="""\ ## Write-only, default = 0x007D0000 ######################################################################## int_n_mode 0x00[31] 0 frac_n, int_n -int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. -frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 +## Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. +int_16_bit 0x00[15:30] 0x007D +## Frac divider: 0-4095 +frac_12_bit 0x00[3:14] 0 ######################################################################## ## Address 0x01 ## Charge pump control @@ -38,8 +40,10 @@ frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 cpoc 0x01[31] 0 disabled, enabled cpl 0x01[29:30] 1 disabled, enabled, res1, res2 cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink -phase_12_bit 0x01[15:26] 1 ##sets phase shift -mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus +## sets phase shift +phase_12_bit 0x01[15:26] 1 +## VCO frac modulus +mod_12_bit 0x01[3:14] 0xFFF ######################################################################## ## Address 0x02 ## Misc. control @@ -50,10 +54,11 @@ low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, muxout 0x02[26:28] 1 tri_state, high, low, rdiv, ndiv, ald, dld, res7 reference_doubler 0x02[25] 0 disabled, enabled reference_divide_by_2 0x02[24] 0 disabled, enabled -r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023 +## R divider value, 1-1023 +r_counter_10_bit 0x02[14:23] 1 double_buffer 0x02[13] 0 disabled, enabled -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) -charge_pump_current 0x02[9:12] 7 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) %>\ +charge_pump_current 0x02[9:12] 7 ${current_setting_enums} ldf 0x02[8] 0 frac_n, int_n ldp 0x02[7] 0 10ns, 6ns pd_polarity 0x02[6] 1 negative, positive @@ -65,18 +70,22 @@ counter_reset 0x02[3] 0 normal, reset ## VCO control ## Write-only, default = 0x0000000B ######################################################################## -vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd -vas 0x03[25] 0 enabled, disabled ##VCO autoselect +## VCO subband selection, used when VAS disabledd +vco 0x03[26:31] 0 +## VCO autoselect +vas 0x03[25] 0 enabled, disabled retune 0x03[24] 1 disabled, enabled clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved -clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095 +## clock divider, 1-4095 +clock_divider_12_bit 0x03[3:14] 1 ######################################################################## ## Address 0x04 ## RF output control ## Write-only, default = 0x6180B23C ######################################################################## res4 0x04[26:31] 0x18 -bs_msb 0x04[24:25] 0 ##Band select MSBs +## Band select MSBs +bs_msb 0x04[24:25] 0 feedback_select 0x04[23] 1 divided, fundamental rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128 band_select_clock_div 0x04[12:19] 0 @@ -111,13 +120,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_max2871_regs.py b/host/lib/ic_reg_maps/gen_max2871_regs.py index 338a019d8..f591c1636 100644..100755 --- a/host/lib/ic_reg_maps/gen_max2871_regs.py +++ b/host/lib/ic_reg_maps/gen_max2871_regs.py @@ -28,8 +28,10 @@ REGS_TMPL="""\ ## Write-only, default = 0x007D0000 ######################################################################## int_n_mode 0x00[31] 0 frac_n, int_n -int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. -frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 +## Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode. +int_16_bit 0x00[15:30] 0x007D +## Frac divider: 0-4095 +frac_12_bit 0x00[3:14] 0 ######################################################################## ## Address 0x01 ## Charge pump control @@ -38,8 +40,10 @@ frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095 res1 0x01[31] 0 cpl 0x01[29:30] 1 disabled, enabled, res1, res2 cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink -phase_12_bit 0x01[15:26] 1 ##sets phase shift -mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus +## sets phase shift +phase_12_bit 0x01[15:26] 1 +## VCO frac modulus +mod_12_bit 0x01[3:14] 0xFFF ######################################################################## ## Address 0x02 ## Misc. control @@ -50,10 +54,11 @@ low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, muxout 0x02[26:28] 0x6 tri_state, high, low, rdiv, ndiv, ald, dld, sync, res8, res9, res10, res11, spi, res13, res14, res15 reference_doubler 0x02[25] 0 disabled, enabled reference_divide_by_2 0x02[24] 0 disabled, enabled -r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023 +## R divider value, 1-1023 +r_counter_10_bit 0x02[14:23] 1 double_buffer 0x02[13] 0 disabled, enabled -#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) -charge_pump_current 0x02[9:12] 7 $current_setting_enums +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16))) %>\ +charge_pump_current 0x02[9:12] 7 ${current_setting_enums} ldf 0x02[8] 0 frac_n, int_n ldp 0x02[7] 0 10ns, 6ns pd_polarity 0x02[6] 1 negative, positive @@ -65,14 +70,17 @@ counter_reset 0x02[3] 0 normal, reset ## VCO control ## Write-only, default = 0x0000000B ######################################################################## -vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd -shutdown_vas 0x03[25] 0 enabled, disabled ##VCO autoselect +## VCO subband selection, used when VAS disabledd +vco 0x03[26:31] 0 +## VCO autoselect +shutdown_vas 0x03[25] 0 enabled, disabled retune 0x03[24] 1 disabled, enabled res3 0x3[19:23] 0 csm 0x3[18] 0 disabled, enabled mutedel 0x3[17] 0 disabled, enabled clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved -clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095 +## clock divider, 1-4095 +clock_divider_12_bit 0x03[3:14] 1 ######################################################################## ## Address 0x04 ## RF output control @@ -82,7 +90,8 @@ res4 0x04[29:31] 0x3 shutdown_ldo 0x04[28] 0 enabled, disabled shutdown_div 0x04[27] 0 enabled, disabled shutdown_ref 0x04[26] 0 enabled, disabled -bs_msb 0x04[24:25] 0 ##Band select MSBs +## Band select MSBs +bs_msb 0x04[24:25] 0 feedback_select 0x04[23] 1 divided, fundamental rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128 band_select_clock_div 0x04[12:19] 0 @@ -124,13 +133,13 @@ enum addr_t{ boost::uint32_t get_reg(boost::uint8_t addr){ boost::uint32_t reg = addr & 0x7; switch(addr){ - #for $addr in range(5+1) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in range(5+1): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return reg; } diff --git a/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py index 677a201de..308d7d524 100755 --- a/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py +++ b/host/lib/ic_reg_maps/gen_tda18272hnm_regs.py @@ -28,7 +28,7 @@ REGS_TMPL="""\ ######################################################################## ## ID_byte_1 (0x00) Read ######################################################################## -##reserved as 1 0x00[7] 1 +## reserved as 1 0x00[7] 1 ident_14_8 0x00[0:6] 0 ######################################################################## ## ID_byte_2 (0x01) Read @@ -43,23 +43,25 @@ minor_rev 0x02[0:3] 0 ######################################################################## ## Thermo_byte_1 (0x03) Read ######################################################################## -##reserved 0x03[7] 0 -tm_d 0x03[0:6] 0 ## 22-127deg C junction temp +## reserved 0x03[7] 0 +## 22-127deg C junction temp +tm_d 0x03[0:6] 0 ######################################################################## ## Thermo_byte_2 (0x04) Write ######################################################################## -##reserved 0x04[1:7] 0 +## reserved 0x04[1:7] 0 tm_on 0x04[0] 0 sensor_off, sensor_on ######################################################################## ## Power_state_byte_1 (0x05) Read ######################################################################## -##reserved 0x05[2:7] 0 +## reserved 0x05[2:7] 0 por 0x05[1] 0 read, reset lo_lock 0x05[0] 0 unlocked, locked ######################################################################## -## Power_state_byte_2 (0x06) Read/Write ## Standby modes +## Standby modes +## Power_state_byte_2 (0x06) Read/Write ######################################################################## -##reserved 0x06[4:7] 0 +## reserved 0x06[4:7] 0 sm 0x06[3] 0 normal, standby sm_pll 0x06[2] 0 on, off sm_lna 0x06[1] 0 on, off @@ -68,14 +70,15 @@ sm_lna 0x06[1] 0 on, off ######################################################################## ## Input_Power_Level_byte (0x07) Read ######################################################################## -##reserved 0x07[7] 0 -power_level 0x07[0:6] 0 ## 40dB_Vrms to 110dB_Vrms +## reserved 0x07[7] 0 +## 40dB_Vrms to 110dB_Vrms +power_level 0x07[0:6] 0 ## Trigger power level calculation with MSM_byte_1 and MSM_byte_2 ######################################################################## ## IRQ_status (0x08) Read/Write ######################################################################## irq_status 0x08[7] 0 cleared, set -##reserved 0x08[6] 0 +## reserved 0x08[6] 0 irq_xtalcal_end 0x08[5] 0 false, true irq_rssi_end 0x08[4] 0 false, true irq_localc_end 0x08[3] 0 false, true @@ -86,7 +89,7 @@ irq_rccal_end 0x08[0] 0 false, true ## IRQ_enable (0x09) Read/Write ######################################################################## irq_enable 0x09[7] 1 false, true -##reserved 0x09[6] 0 +## reserved 0x09[6] 0 irq_xtalcal_enable 0x09[5] 0 false, true irq_rssi_enable 0x09[4] 0 false, true irq_localc_enable 0x09[3] 0 false, true @@ -97,7 +100,7 @@ irq_rccal_enable 0x09[0] 0 false, true ## IRQ_clear (0x0a) Read/Write ######################################################################## irq_clear 0x0a[7] 0 false, true -##reserved 0x0a[6] 0 +## reserved 0x0a[6] 0 irq_xtalcal_clear 0x0a[5] 0 false, true irq_rssi_clear 0x0a[4] 0 false, true irq_localc_clear 0x0a[3] 0 false, true @@ -108,7 +111,7 @@ irq_rccal_clear 0x0a[0] 0 false, true ## IRQ_set (0x0b) Read ######################################################################## irq_set 0x0b[7] 0 false, true -##reserved 0x0b[6] 0 +## reserved 0x0b[6] 0 irq_xtalcal_set 0x0b[5] 0 false, true irq_rssi_set 0x0b[4] 0 false, true irq_localc_set 0x0b[3] 0 false, true @@ -120,12 +123,12 @@ irq_rccal_set 0x0b[0] 0 false, true ######################################################################## lt_enable 0x0c[7] 0 agc1_6_15db 0x0c[6] 1 -##reserved 0x0c[4:5] 0 +## reserved 0x0c[4:5] 0 agc1_top 0x0c[0:3] 0 ######################################################################## ## AGC2_byte_1 (0x0d) Read ######################################################################## -##reserved 0x0d[5:7] 0 +## reserved 0x0d[5:7] 0 agc2_top 0x0d[0:4] 0xf ######################################################################## ## AGCK_byte_1 (0x0e) Read/Write @@ -141,24 +144,25 @@ agck_mode 0x0e[0:1] 1 analog_tv=1, digital_tv=2 pd_rfagc_adapt 0x0f[7] 0 on, off rfagc_adapt_top 0x0f[5:6] 0 rfagc_low_bw 0x0f[4] 1 -rf_atten_3db 0x0f[3] 0 0db, 3db ## FIXME +## FIXME +rf_atten_3db 0x0f[3] 0 0db, 3db agc3_top 0x0f[0:2] 1 ######################################################################## ## IR_MIXER_byte_1 (0x10) Read/Write ######################################################################## -##reserved 0x10[4:7] 0 +## reserved 0x10[4:7] 0 agc4_top 0x10[0:3] 1 ######################################################################## ## AGC5_byte_1 (0x11) Read/Write ######################################################################## -##reserved 0x11[7] 0 +## reserved 0x11[7] 0 agcs_do_step_assym 0x11[5:6] 2 agc5_hpf 0x11[4] 1 off, on agc5_top 0x11[0:3] 1 ######################################################################## ## IF_AGC_byte (0x12) Read/Write ######################################################################## -##reserved 0x12[3:7] 0 +## reserved 0x12[3:7] 0 if_level 0x12[0:2] 0 0_5vpp=7, 0_6vpp=6, 0_7vpp=5, 0_85vpp=4, 0_8vpp=3, 1_0vpp=2, 1_25vpp=1, 2_0vpp=0 ######################################################################## ## IF_byte_1 (0x13) Read/Write @@ -172,18 +176,19 @@ lp_fc 0x13[0:2] 3 1_7mhz=4, 6_0mhz=0, 7_0mhz=1, 8_0mhz=2, ######################################################################## i2c_clock_mode 0x14[7] 0 digital_clock 0x14[6] 1 spread_off, spread_on -##reserved 0x14[5] 0 +## reserved 0x14[5] 0 xtalosc_anareg_en 0x14[4] 0 -##reserved 0x14[2:3] 0 +## reserved 0x14[2:3] 0 xtout 0x14[0:1] 0 no=0, 16mhz=3 ######################################################################## ## IF_Frequency_byte (0x15) Read/Write ######################################################################## -if_freq 0x15[0:7] 0 ## IF frequency = if_freq*50 (kHz) +## IF frequency = if_freq*50 (kHz) +if_freq 0x15[0:7] 0 ######################################################################## ## RF_Frequency_byte_1 (0x16) Read/Write ######################################################################## -##reserved 0x16[4:7] 0 +## reserved 0x16[4:7] 0 rf_freq_19_16 0x16[0:3] 0 ######################################################################## ## RF_Frequency_byte_2 (0x17) Read/Write @@ -209,7 +214,7 @@ calc_pll 0x19[0] 0 ######################################################################## ## MSM_byte_2 (0x1a) Read ######################################################################## -##reserved 0x1a[2:7] 0 +## reserved 0x1a[2:7] 0 xtalcal_launch 0x1a[1] 0 msm_launch 0x1a[0] 0 ######################################################################## @@ -227,11 +232,11 @@ psm_lodriver 0x1b[0:1] 0 dcc_bypass 0x1c[7] 0 dcc_slow 0x1c[6] 0 dcc_psm 0x1c[5] 0 -##reserved 0x1c[0:4] 0 +## reserved 0x1c[0:4] 0 ######################################################################## ## FLO_Max_byte (0x1d) Read ######################################################################## -##reserved 0x1d[6:7] 0 +## reserved 0x1d[6:7] 0 fmax_lo 0x1d[0:5] 0xA ######################################################################## ## IR_Cal_byte_1 (0x1e) Read @@ -249,12 +254,12 @@ ir_freqlow 0x1f[0:4] 0 ######################################################################## ## IR_Cal_byte_3 (0x20) Read ######################################################################## -##reserved 0x20[5:7] 0 +## reserved 0x20[5:7] 0 ir_freqmid 0x20[0:4] 0 ######################################################################## ## IR_Cal_byte_4 (0x21) Read ######################################################################## -##reserved 0x21[5:7] 0 +## reserved 0x21[5:7] 0 coarse_ir_freqhigh 0x21[4] 0 ir_freqhigh 0x21[0:3] 0 ######################################################################## @@ -270,8 +275,9 @@ agc_ovld_timer 0x22[0:1] 0 ######################################################################## ir_mixer_loop_off 0x23[7] 0 ir_mixer_do_step 0x23[5:6] 0 -##reserved 0x23[2:4] 0 -hi_pass 0x23[1] 0 disable, enable ## FIXME Logic Unclear +## reserved 0x23[2:4] 0 +## FIXME Logic Unclear +hi_pass 0x23[1] 0 disable, enable if_notch 0x23[0] 1 on, off ######################################################################## ## AGC1_byte_2 (0x24) Read @@ -285,9 +291,9 @@ agc1_gain 0x24[0:3] 8 ######################################################################## agc5_loop_off 0x25[7] 0 agc5_do_step 0x25[5:6] 0 -##reserved 0x25[4] 0 +## reserved 0x25[4] 0 force_agc5_gain 0x25[3] 0 -##reserved 0x25[2] 0 +## reserved 0x25[2] 0 agc5_gain 0x25[0:1] 2 ######################################################################## ## RF_Cal_byte_1 (0x26) Read @@ -335,7 +341,7 @@ rfcal_freq11 0x2b[0:1] 0 ## RF_Filter_byte_1 (0x2c) Read ######################################################################## rf_filter_bypass 0x2c[7] 0 -##reserved as 0 0x2c[6] 0 +## reserved as 0 0x2c[6] 0 agc2_loop_off 0x2c[5] 0 force_agc2_gain 0x2c[4] 0 rf_filter_gv 0x2c[2:3] 2 @@ -353,12 +359,12 @@ gain_taper 0x2e[0:5] 0 ## RF_Band_Pass_Filter_byte (0x2f) Read ######################################################################## rf_bpf_bypass 0x2f[7] 0 -##reserved 0x2f[3:6] 0 +## reserved 0x2f[3:6] 0 rf_bpf 0x2f[0:2] 0 ######################################################################## ## CP_Current_byte (0x30) Read ######################################################################## -##reserved 0x30[7] 0 +## reserved 0x30[7] 0 n_cp_current 0x30[0:6] 0x68 ######################################################################## ## AGC_Det_Out_byte (0x31) Read @@ -374,24 +380,26 @@ do_agc1 0x31[0] 0 ######################################################################## ## RF_AGC_Gain_byte_1 (0x32) Read ######################################################################## -#set $lna_gain_names = ', '.join(map(lambda x: {0: '', 1: 'm'}[3*x-12 < 0] + str(abs(3*x-12)) + 'db=' + str(x), range(0,10))) -##reserved 0x32[6:7] 0 +## reserved 0x32[6:7] 0 agc2_gain_read 0x32[4:5] 3 m11db, m8db, m5db, m2db -agc1_gain_read 0x32[0:3] 9 $lna_gain_names +<% lna_gain_names = ', '.join(map(lambda x: {0: '', 1: 'm'}[3*x-12 < 0] + str(abs(3*x-12)) + 'db=' + str(x), range(0,10))) %>\ +agc1_gain_read 0x32[0:3] 9 ${lna_gain_names} ######################################################################## ## RF_AGC_Gain_byte_2 (0x33) Read ######################################################################## -#set $top_agc3_read_names = ', '.join(map(lambda x: str(int(round(1.92*x+94))) + 'dbuvrms=' + str(x), range(0,8))) -##reserved 0x33[3:7] 0 -top_agc3_read 0x33[0:2] 0 $top_agc3_read_names +## reserved 0x33[3:7] 0 +<% top_agc3_read_names = ', '.join(map(lambda x: str(int(round(1.92*x+94))) + 'dbuvrms=' + str(x), range(0,8))) %>\ +top_agc3_read 0x33[0:2] 0 ${top_agc3_read_names} ######################################################################## ## IF_AGC_Gain_byte (0x34) Read ######################################################################## -#set $lpf_gain_names = ', '.join(map(lambda x: str(3*x) + 'db=' + str(x), range(0,4))) -#set $ir_mixer_names = ', '.join(map(lambda x: str(3*x+2) + 'db=' + str(x), range(0,5))) -##reserved 0x34[5:7] 0 -agc5_gain_read 0x34[3:4] 3 $lpf_gain_names -agc4_gain_read 0x34[0:2] 4 $ir_mixer_names +## reserved 0x34[5:7] 0 +<% + lpf_gain_names = ', '.join(map(lambda x: str(3*x) + 'db=' + str(x), range(0,4))) + ir_mixer_names = ', '.join(map(lambda x: str(3*x+2) + 'db=' + str(x), range(0,5))) +%>\ +agc5_gain_read 0x34[3:4] 3 ${lpf_gain_names} +agc4_gain_read 0x34[0:2] 4 ${ir_mixer_names} ######################################################################## ## Power_byte_1 (0x35) Read ######################################################################## @@ -399,9 +407,9 @@ rssi 0x35[0:7] 0 ######################################################################## ## Power_byte_2 (0x36) Read ######################################################################## -##reserved 0x36[6:7] 0 +## reserved 0x36[6:7] 0 rssi_av 0x36[5] 0 -##reserved 0x36[4] 0 +## reserved 0x36[4] 0 rssi_cap_reset_en 0x36[3] 1 rssi_cap_val 0x36[2] 1 rssi_ck_speed 0x36[1] 0 @@ -479,39 +487,30 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } void set_reg(boost::uint8_t addr, boost::uint8_t reg){ switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + ${reg.get_name()} = ${reg.get_type()}((reg >> ${reg.get_shift()}) & ${reg.get_mask()}); + % endfor break; - #end for + % endfor } } """ -SPLIT_REGS_HELPER_TMPL="""\ -#for $divname in ['n','f'] -void set_$(divname)_divider(boost::uint32_t $divname){ - #for $regname in sorted(map(lambda r: r.get_name(), filter(lambda r: r.get_name().find(divname + '_divider') == 0, $regs))) - #end for -} -#end for -""" - if __name__ == '__main__': import common; common.generate( name='tda18272hnm_regs', diff --git a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py index 73f7aa3db..9b8e1958f 100644..100755 --- a/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py +++ b/host/lib/ic_reg_maps/gen_tuner_4937di5_regs.py @@ -53,13 +53,13 @@ BODY_TMPL="""\ boost::uint8_t get_reg(boost::uint8_t addr){ boost::uint8_t reg = 0; switch(addr){ - #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) - case $addr: - #for $reg in filter(lambda r: r.get_addr() == addr, $regs) - reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift(); - #end for + % for addr in sorted(set(map(lambda r: r.get_addr(), regs))): + case ${addr}: + % for reg in filter(lambda r: r.get_addr() == addr, regs): + reg |= (boost::uint8_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; + % endfor break; - #end for + % endfor } return boost::uint8_t(reg); } diff --git a/host/lib/image_loader.cpp b/host/lib/image_loader.cpp new file mode 100644 index 000000000..91dd325dd --- /dev/null +++ b/host/lib/image_loader.cpp @@ -0,0 +1,87 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <map> +#include <utility> + +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> + +#include <uhd/exception.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/static.hpp> + +namespace fs = boost::filesystem; + +typedef std::map<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_map_t; +typedef std::map<std::string, std::string> string_map_t; + +// Nice typedefs for iterating over std::map +typedef std::pair<std::string, uhd::image_loader::loader_fcn_t> loader_fcn_pair_t; +typedef std::pair<std::string, std::string> string_pair_t; + +UHD_SINGLETON_FCN(loader_fcn_map_t, get_image_loaders); +UHD_SINGLETON_FCN(string_map_t, get_recovery_strings); + +/* + * Registration + */ +void uhd::image_loader::register_image_loader(const std::string &device_type, + const loader_fcn_t &loader_fcn, + const std::string &recovery_instructions){ + UHD_LOGV(always) << "Registering image loader and recovery instructions for " + << device_type << std::endl; + + get_image_loaders().insert(loader_fcn_pair_t(device_type, loader_fcn)); + get_recovery_strings().insert(string_pair_t(device_type, recovery_instructions)); +} + +/* + * Actual loading + */ +bool uhd::image_loader::load(const uhd::image_loader::image_loader_args_t &image_loader_args){ + + // If "type=foo" given in args, see if we have an image loader for that + if(image_loader_args.args.has_key("type")){ + std::string type = image_loader_args.args.get("type"); + if(get_image_loaders().find(type) == get_image_loaders().end()){ + throw uhd::runtime_error(str(boost::format("There is no image loader registered for given type \"%s\".") + % type)); + } + else return get_image_loaders().at(type)(image_loader_args); + } + else{ + BOOST_FOREACH(const loader_fcn_pair_t &loader_fcn_pair, get_image_loaders()){ + if(loader_fcn_pair.second(image_loader_args)) return true; + } + return false; + } +} + +/* + * Get recovery instructions for particular device + */ +std::string uhd::image_loader::get_recovery_instructions(const std::string &device_type){ + if(get_recovery_strings().count(device_type) == 0){ + return "A firmware or FPGA loading process was interrupted by the user. This can leave your device in a non-working state."; + } + else return get_recovery_strings().at(device_type); +} diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 5920f3d78..6abc399b4 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2013,2015 Ettus Research LLC # # 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 @@ -30,7 +30,7 @@ INCLUDE_SUBDIRECTORY(nirio) MESSAGE(STATUS "") FIND_PACKAGE(USB1) -LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF) +LIBUHD_REGISTER_COMPONENT("USB" ENABLE_USB ON "ENABLE_LIBUHD;LIBUSB_FOUND" OFF OFF) IF(ENABLE_USB) MESSAGE(STATUS "USB support enabled via libusb.") @@ -129,6 +129,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/if_addrs.cpp ${CMAKE_CURRENT_SOURCE_DIR}/udp_simple.cpp ${CMAKE_CURRENT_SOURCE_DIR}/nirio_zero_copy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/chdr.cpp ) # Verbose Debug output for send/recv diff --git a/host/lib/transport/chdr.cpp b/host/lib/transport/chdr.cpp new file mode 100644 index 000000000..632887e56 --- /dev/null +++ b/host/lib/transport/chdr.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/exception.hpp> + +//define the endian macros to convert integers +#ifdef BOOST_BIG_ENDIAN + #define BE_MACRO(x) (x) + #define LE_MACRO(x) uhd::byteswap(x) +#else + #define BE_MACRO(x) uhd::byteswap(x) + #define LE_MACRO(x) (x) +#endif + +using namespace uhd::transport::vrt; + +static const boost::uint32_t HDR_FLAG_TSF = (1 << 29); +static const boost::uint32_t HDR_FLAG_EOB = (1 << 28); +static const boost::uint32_t HDR_FLAG_ERROR = (1 << 28); + +/***************************************************************************/ +/* Packing */ +/***************************************************************************/ +/*! Translate the contents of \p if_packet_info into a 32-Bit word and return it. + */ +UHD_INLINE boost::uint32_t _hdr_pack_chdr( + if_packet_info_t &if_packet_info +) { + // Set fields in if_packet_info + if_packet_info.num_header_words32 = 2 + (if_packet_info.has_tsf ? 2 : 0); + if_packet_info.num_packet_words32 = + if_packet_info.num_header_words32 + + if_packet_info.num_payload_words32; + + boost::uint16_t pkt_length = + if_packet_info.num_payload_bytes + (4 * if_packet_info.num_header_words32); + boost::uint32_t chdr = 0 + // 2 Bits: Packet type + | (if_packet_info.packet_type << 30) + // 1 Bit: Has time + | (if_packet_info.has_tsf ? HDR_FLAG_TSF : 0) + // 1 Bit: EOB or Error + | ((if_packet_info.eob or if_packet_info.error) ? HDR_FLAG_EOB : 0) + // 12 Bits: Sequence number + | ((if_packet_info.packet_count & 0xFFF) << 16) + // 16 Bits: Total packet length + | pkt_length; + return chdr; +} + +void chdr::if_hdr_pack_be( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = BE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = BE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = BE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + +void chdr::if_hdr_pack_le( + boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Write header and update if_packet_info + packet_buff[0] = LE_MACRO(_hdr_pack_chdr(if_packet_info)); + + // Write SID + packet_buff[1] = LE_MACRO(if_packet_info.sid); + + // Write time + if (if_packet_info.has_tsf) { + packet_buff[2] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 32)); + packet_buff[3] = LE_MACRO(boost::uint32_t(if_packet_info.tsf >> 0)); + } +} + + +/***************************************************************************/ +/* Unpacking */ +/***************************************************************************/ +UHD_INLINE void _hdr_unpack_chdr( + const boost::uint32_t chdr, + if_packet_info_t &if_packet_info +) { + // Set constant members + if_packet_info.link_type = if_packet_info_t::LINK_TYPE_CHDR; + if_packet_info.has_cid = false; + if_packet_info.has_sid = true; + if_packet_info.has_tsi = false; + if_packet_info.has_tlr = false; + if_packet_info.sob = false; + + // Set configurable members + if_packet_info.has_tsf = (chdr & HDR_FLAG_TSF) > 0; + if_packet_info.packet_type = if_packet_info_t::packet_type_t((chdr >> 30) & 0x3); + if_packet_info.eob = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_DATA) + && ((chdr & HDR_FLAG_EOB) > 0); + if_packet_info.error = (if_packet_info.packet_type == if_packet_info_t::PACKET_TYPE_RESP) + && ((chdr & HDR_FLAG_ERROR) > 0); + if_packet_info.packet_count = (chdr >> 16) & 0xFFF; + + // Set packet length variables + if (if_packet_info.has_tsf) { + if_packet_info.num_header_words32 = 4; + } else { + if_packet_info.num_header_words32 = 2; + } + size_t pkt_size_bytes = (chdr & 0xFFFF); + size_t pkt_size_word32 = (pkt_size_bytes / 4) + ((pkt_size_bytes % 4) ? 1 : 0); + // Check lengths match: + if (pkt_size_word32 < if_packet_info.num_header_words32) { + throw uhd::value_error("Bad CHDR or invalid packet length"); + } + if (if_packet_info.num_packet_words32 < pkt_size_word32) { + throw uhd::value_error("Bad CHDR or packet fragment"); + } + if_packet_info.num_payload_bytes = pkt_size_bytes - (4 * if_packet_info.num_header_words32); + if_packet_info.num_payload_words32 = pkt_size_word32 - if_packet_info.num_header_words32; +} + +void chdr::if_hdr_unpack_be( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = BE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = BE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(BE_MACRO(packet_buff[2])) << 32 + | BE_MACRO(packet_buff[3]); + } +} + +void chdr::if_hdr_unpack_le( + const boost::uint32_t *packet_buff, + if_packet_info_t &if_packet_info +) { + // Read header and update if_packet_info + boost::uint32_t chdr = LE_MACRO(packet_buff[0]); + _hdr_unpack_chdr(chdr, if_packet_info); + + // Read SID + if_packet_info.sid = LE_MACRO(packet_buff[1]); + + // Read time (has_tsf was updated earlier) + if (if_packet_info.has_tsf) { + if_packet_info.tsf = 0 + | boost::uint64_t(LE_MACRO(packet_buff[2])) << 32 + | LE_MACRO(packet_buff[3]); + } +} + diff --git a/host/lib/transport/gen_vrt_if_packet.py b/host/lib/transport/gen_vrt_if_packet.py index 98f6804ae..6723e3a4b 100644 --- a/host/lib/transport/gen_vrt_if_packet.py +++ b/host/lib/transport/gen_vrt_if_packet.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2013,2015 Ettus Research LLC # # 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 @@ -25,26 +25,25 @@ metatdata into vrt headers and vrt headers into metadata. The generated code infers jump tables to speed-up the parsing time. """ -TMPL_TEXT = """ -#import time +TMPL_TEXT = """<% import time %> /*********************************************************************** - * This file was generated by $file on $time.strftime("%c") + * This file was generated by ${file} on ${time.strftime("%c")} **********************************************************************/ -\#include <uhd/exception.hpp> -\#include <uhd/transport/vrt_if_packet.hpp> -\#include <uhd/utils/byteswap.hpp> -\#include <boost/detail/endian.hpp> -\#include <vector> +#include <uhd/exception.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/detail/endian.hpp> +#include <vector> //define the endian macros to convert integers -\#ifdef BOOST_BIG_ENDIAN - \#define BE_MACRO(x) (x) - \#define LE_MACRO(x) uhd::byteswap(x) -\#else - \#define BE_MACRO(x) uhd::byteswap(x) - \#define LE_MACRO(x) (x) -\#endif +#ifdef BOOST_BIG_ENDIAN + #define BE_MACRO(x) (x) + #define LE_MACRO(x) uhd::byteswap(x) +#else + #define BE_MACRO(x) uhd::byteswap(x) + #define LE_MACRO(x) (x) +#endif using namespace uhd; using namespace uhd::transport; @@ -59,13 +58,13 @@ static pred_table_type get_pred_unpack_table(void) pred_table_type table(1 << 9, 0); //only 9 bits useful here (20-28) for (size_t i = 0; i < table.size(); i++){ boost::uint32_t vrt_hdr_word = i << 20; - if(vrt_hdr_word & $hex(0x1 << 28)) table[i] |= $hex($sid_p); - if(vrt_hdr_word & $hex(0x1 << 27)) table[i] |= $hex($cid_p); - if(vrt_hdr_word & $hex(0x3 << 22)) table[i] |= $hex($tsi_p); - if(vrt_hdr_word & $hex(0x3 << 20)) table[i] |= $hex($tsf_p); - if(vrt_hdr_word & $hex(0x1 << 26)) table[i] |= $hex($tlr_p); - if(vrt_hdr_word & $hex(0x1 << 24)) table[i] |= $hex($eob_p); - if(vrt_hdr_word & $hex(0x1 << 25)) table[i] |= $hex($sob_p); + if(vrt_hdr_word & ${hex(0x1 << 28)}) table[i] |= ${hex(sid_p)}; + if(vrt_hdr_word & ${hex(0x1 << 27)}) table[i] |= ${hex(cid_p)}; + if(vrt_hdr_word & ${hex(0x3 << 22)}) table[i] |= ${hex(tsi_p)}; + if(vrt_hdr_word & ${hex(0x3 << 20)}) table[i] |= ${hex(tsf_p)}; + if(vrt_hdr_word & ${hex(0x1 << 26)}) table[i] |= ${hex(tlr_p)}; + if(vrt_hdr_word & ${hex(0x1 << 24)}) table[i] |= ${hex(eob_p)}; + if(vrt_hdr_word & ${hex(0x1 << 25)}) table[i] |= ${hex(sob_p)}; } return table; } @@ -105,13 +104,12 @@ UHD_INLINE static boost::uint32_t vrt_to_chdr(const boost::uint32_t vrt, const i } ######################################################################## -#def gen_code($XE_MACRO, $suffix) +<%def name="gen_code(XE_MACRO, suffix)"> ######################################################################## - /*********************************************************************** - * interal impl of packing VRT IF header only + * internal impl of packing VRT IF header only **********************************************************************/ -UHD_INLINE void __if_hdr_pack_$(suffix)( +UHD_INLINE void __if_hdr_pack_${suffix}( boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info, boost::uint32_t &vrt_hdr_word32 @@ -119,72 +117,53 @@ UHD_INLINE void __if_hdr_pack_$(suffix)( boost::uint32_t vrt_hdr_flags = 0; pred_type pred = 0; - if (if_packet_info.has_sid) pred |= $hex($sid_p); - if (if_packet_info.has_cid) pred |= $hex($cid_p); - if (if_packet_info.has_tsi) pred |= $hex($tsi_p); - if (if_packet_info.has_tsf) pred |= $hex($tsf_p); - if (if_packet_info.has_tlr) pred |= $hex($tlr_p); - if (if_packet_info.eob) pred |= $hex($eob_p); - if (if_packet_info.sob) pred |= $hex($sob_p); + if (if_packet_info.has_sid) pred |= ${hex(sid_p)}; + if (if_packet_info.has_cid) pred |= ${hex(cid_p)}; + if (if_packet_info.has_tsi) pred |= ${hex(tsi_p)}; + if (if_packet_info.has_tsf) pred |= ${hex(tsf_p)}; + if (if_packet_info.has_tlr) pred |= ${hex(tlr_p)}; + if (if_packet_info.eob) pred |= ${hex(eob_p)}; + if (if_packet_info.sob) pred |= ${hex(sob_p)}; switch(pred){ - #for $pred in range(2**7) - case $pred: - #set $num_header_words = 1 - #set $flags = 0 + % for pred in range(2**7): + case ${pred}:<% num_header_words = 1 %><% flags = 0 %> ########## Stream ID ########## - #if $pred & $sid_p - packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.sid); - #set $num_header_words += 1 - #set $flags |= (0x1 << 28); - #end if + % if pred & sid_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.sid);<% num_header_words += 1 %><% flags |= (0x1 << 28) %> + % endif ########## Class ID ########## - #if $pred & $cid_p - packet_buff[$num_header_words] = 0; //not implemented - #set $num_header_words += 1 - packet_buff[$num_header_words] = 0; //not implemented - #set $num_header_words += 1 - #set $flags |= (0x1 << 27); - #end if + % if pred & cid_p: + packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %> + packet_buff[${num_header_words}] = 0; //not implemented<% num_header_words += 1 %><% flags |= (0x1 << 27) %> + % endif ########## Integer Time ########## - #if $pred & $tsi_p - packet_buff[$num_header_words] = $(XE_MACRO)(if_packet_info.tsi); - #set $num_header_words += 1 - #set $flags |= (0x3 << 22); - #end if + % if pred & tsi_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(if_packet_info.tsi);<% num_header_words += 1 %><% flags |= (0x3 << 22) %> + % endif ########## Fractional Time ########## - #if $pred & $tsf_p - packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 32)); - #set $num_header_words += 1 - packet_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(if_packet_info.tsf >> 0)); - #set $num_header_words += 1 - #set $flags |= (0x1 << 20); - #end if + % if pred & tsf_p: + packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 32));<% num_header_words += 1 %> + packet_buff[${num_header_words}] = ${XE_MACRO}(boost::uint32_t(if_packet_info.tsf >> 0));<% num_header_words += 1 %><% flags |= (0x1 << 20) %> + % endif ########## Burst Flags ########## - #if $pred & $eob_p - #set $flags |= (0x1 << 24); - #end if - #if $pred & $sob_p - #set $flags |= (0x1 << 25); - #end if +<% if pred & eob_p: flags |= (0x1 << 24) %><% if pred & sob_p: flags |= (0x1 << 25) %> ########## Trailer ########## - #if $pred & $tlr_p + % if pred & tlr_p: { const size_t empty_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - if_packet_info.num_payload_bytes; if_packet_info.tlr = (0x3 << 22) | (occ_table[empty_bytes & 0x3] << 10); } - packet_buff[$num_header_words+if_packet_info.num_payload_words32] = $(XE_MACRO)(if_packet_info.tlr); - #set $flags |= (0x1 << 26); - #set $num_trailer_words = 1; - #else - #set $num_trailer_words = 0; - #end if + packet_buff[${num_header_words}+if_packet_info.num_payload_words32] = ${XE_MACRO}(if_packet_info.tlr);<% flags |= (0x1 << 26) %><% num_trailer_words = 1 %> + % else: +<% num_trailer_words = 0 %> + % endif ########## Variables ########## - if_packet_info.num_header_words32 = $num_header_words; - if_packet_info.num_packet_words32 = $($num_header_words + $num_trailer_words) + if_packet_info.num_payload_words32; - vrt_hdr_flags = $hex($flags); + if_packet_info.num_header_words32 = ${num_header_words}; + if_packet_info.num_packet_words32 = ${num_header_words + num_trailer_words} + if_packet_info.num_payload_words32; + vrt_hdr_flags = ${hex(flags)}; break; - #end for + % endfor } //fill in complete header word @@ -197,9 +176,9 @@ UHD_INLINE void __if_hdr_pack_$(suffix)( } /*********************************************************************** - * interal impl of unpacking VRT IF header only + * internal impl of unpacking VRT IF header only **********************************************************************/ -UHD_INLINE void __if_hdr_unpack_$(suffix)( +UHD_INLINE void __if_hdr_unpack_${suffix}( const boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info, const boost::uint32_t vrt_hdr_word32 @@ -219,86 +198,78 @@ UHD_INLINE void __if_hdr_unpack_$(suffix)( size_t empty_bytes = 0; switch(pred){ - #for $pred in range(2**7) - case $pred: - #set $has_time_spec = False - #set $num_header_words = 1 + % for pred in range(2**7): + case ${pred}:<% has_time_spec = False %><% num_header_words = 1 %> ########## Stream ID ########## - #if $pred & $sid_p + % if pred & sid_p: if_packet_info.has_sid = true; - if_packet_info.sid = $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.sid = ${XE_MACRO}(packet_buff[${num_header_words}]);<% num_header_words += 1 %> + % else: if_packet_info.has_sid = false; - #end if + % endif ########## Class ID ########## - #if $pred & $cid_p + % if pred & cid_p: if_packet_info.has_cid = true; - if_packet_info.cid = 0; //not implemented - #set $num_header_words += 2 - #else + if_packet_info.cid = 0; //not implemented<% num_header_words += 2 %> + % else: if_packet_info.has_cid = false; - #end if + % endif ########## Integer Time ########## - #if $pred & $tsi_p + % if pred & tsi_p: if_packet_info.has_tsi = true; - if_packet_info.tsi = $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.tsi = ${XE_MACRO}(packet_buff[${num_header_words}]); +<% num_header_words += 1 %> + % else: if_packet_info.has_tsi = false; - #end if + % endif ########## Fractional Time ########## - #if $pred & $tsf_p + % if pred & tsf_p: if_packet_info.has_tsf = true; - if_packet_info.tsf = boost::uint64_t($(XE_MACRO)(packet_buff[$num_header_words])) << 32; - #set $num_header_words += 1 - if_packet_info.tsf |= $(XE_MACRO)(packet_buff[$num_header_words]); - #set $num_header_words += 1 - #else + if_packet_info.tsf = boost::uint64_t(${XE_MACRO}(packet_buff[${num_header_words}])) << 32;<% num_header_words += 1 %> + if_packet_info.tsf |= ${XE_MACRO}(packet_buff[${num_header_words}]);<% num_header_words += 1 %> + % else: if_packet_info.has_tsf = false; - #end if + % endif ########## Burst Flags ########## - #if $pred & $eob_p + % if pred & eob_p: if_packet_info.eob = true; - #else + % else: if_packet_info.eob = false; - #end if - #if $pred & $sob_p + % endif + % if pred & sob_p: if_packet_info.sob = true; - #else + % else: if_packet_info.sob = false; - #end if + % endif ########## Trailer ########## - #if $pred & $tlr_p + % if pred & tlr_p: if_packet_info.has_tlr = true; - if_packet_info.tlr = $(XE_MACRO)(packet_buff[packet_words32-1]); - #set $num_trailer_words = 1; + if_packet_info.tlr = ${XE_MACRO}(packet_buff[packet_words32-1]);<% num_trailer_words = 1 %> { const int indicators = (if_packet_info.tlr >> 20) & (if_packet_info.tlr >> 8); if ((indicators & (1 << 0)) != 0) if_packet_info.eob = true; if ((indicators & (1 << 1)) != 0) if_packet_info.sob = true; empty_bytes = occ_table[(indicators >> 2) & 0x3]; } - #else - if_packet_info.has_tlr = false; - #set $num_trailer_words = 0; - #end if + % else: + if_packet_info.has_tlr = false;<% num_trailer_words = 0 %> + % endif ########## Variables ########## //another failure case - if (packet_words32 < $($num_header_words + $num_trailer_words)) + if (packet_words32 < ${num_header_words + num_trailer_words}) throw uhd::value_error("bad vrt header or invalid packet length"); - if_packet_info.num_header_words32 = $num_header_words; - if_packet_info.num_payload_words32 = packet_words32 - $($num_header_words + $num_trailer_words); + if_packet_info.num_header_words32 = ${num_header_words}; + if_packet_info.num_payload_words32 = packet_words32 - ${num_header_words + num_trailer_words}; if_packet_info.num_payload_bytes = if_packet_info.num_payload_words32*sizeof(boost::uint32_t) - empty_bytes; break; - #end for + % endfor } } /*********************************************************************** * link layer + VRT IF packing **********************************************************************/ -void vrt::if_hdr_pack_$(suffix)( +void vrt::if_hdr_pack_${suffix}( boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info ){ @@ -306,29 +277,29 @@ void vrt::if_hdr_pack_$(suffix)( switch (if_packet_info.link_type) { case if_packet_info_t::LINK_TYPE_NONE: - __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); - packet_buff[0] = $(XE_MACRO)(vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); + packet_buff[0] = ${XE_MACRO}(vrt_hdr_word32); break; case if_packet_info_t::LINK_TYPE_CHDR: { - __if_hdr_pack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); const boost::uint32_t chdr = vrt_to_chdr(vrt_hdr_word32, if_packet_info); - packet_buff[0] = $(XE_MACRO)(chdr); + packet_buff[0] = ${XE_MACRO}(chdr); break; } case if_packet_info_t::LINK_TYPE_VRLP: - __if_hdr_pack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + __if_hdr_pack_${suffix}(packet_buff+2, if_packet_info, vrt_hdr_word32); if_packet_info.num_header_words32 += 2; if_packet_info.num_packet_words32 += 3; - packet_buff[0] = $(XE_MACRO)(VRLP); - packet_buff[1] = $(XE_MACRO)(boost::uint32_t( + packet_buff[0] = ${XE_MACRO}(VRLP); + packet_buff[1] = ${XE_MACRO}(boost::uint32_t( (if_packet_info.num_packet_words32 & 0xfffff) | ((if_packet_info.packet_count & 0xfff) << 20) )); - packet_buff[2] = $(XE_MACRO)(vrt_hdr_word32); - packet_buff[if_packet_info.num_packet_words32-1] = $(XE_MACRO)(VEND); + packet_buff[2] = ${XE_MACRO}(vrt_hdr_word32); + packet_buff[if_packet_info.num_packet_words32-1] = ${XE_MACRO}(VEND); break; } } @@ -336,7 +307,7 @@ void vrt::if_hdr_pack_$(suffix)( /*********************************************************************** * link layer + VRT IF unpacking **********************************************************************/ -void vrt::if_hdr_unpack_$(suffix)( +void vrt::if_hdr_unpack_${suffix}( const boost::uint32_t *packet_buff, if_packet_info_t &if_packet_info ){ @@ -344,16 +315,16 @@ void vrt::if_hdr_unpack_$(suffix)( switch (if_packet_info.link_type) { case if_packet_info_t::LINK_TYPE_NONE: - vrt_hdr_word32 = $(XE_MACRO)(packet_buff[0]); - __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + vrt_hdr_word32 = ${XE_MACRO}(packet_buff[0]); + __if_hdr_unpack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); break; case if_packet_info_t::LINK_TYPE_CHDR: { - const boost::uint32_t chdr = $(XE_MACRO)(packet_buff[0]); + const boost::uint32_t chdr = ${XE_MACRO}(packet_buff[0]); vrt_hdr_word32 = chdr_to_vrt(chdr, if_packet_info); size_t packet_count = if_packet_info.packet_count; - __if_hdr_unpack_$(suffix)(packet_buff, if_packet_info, vrt_hdr_word32); + __if_hdr_unpack_${suffix}(packet_buff, if_packet_info, vrt_hdr_word32); if_packet_info.num_payload_bytes -= (~chdr + 1) & 0x3; if_packet_info.packet_count = packet_count; break; @@ -361,12 +332,12 @@ void vrt::if_hdr_unpack_$(suffix)( case if_packet_info_t::LINK_TYPE_VRLP: { - if ($(XE_MACRO)(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP"); - const boost::uint32_t vrl_hdr = $(XE_MACRO)(packet_buff[1]); - vrt_hdr_word32 = $(XE_MACRO)(packet_buff[2]); + if (${XE_MACRO}(packet_buff[0]) != VRLP) throw uhd::value_error("bad vrl header VRLP"); + const boost::uint32_t vrl_hdr = ${XE_MACRO}(packet_buff[1]); + vrt_hdr_word32 = ${XE_MACRO}(packet_buff[2]); if (if_packet_info.num_packet_words32 < (vrl_hdr & 0xfffff)) throw uhd::value_error("bad vrl header or packet fragment"); - if ($(XE_MACRO)(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND"); - __if_hdr_unpack_$(suffix)(packet_buff+2, if_packet_info, vrt_hdr_word32); + if (${XE_MACRO}(packet_buff[(vrl_hdr & 0xfffff)-1]) != VEND) throw uhd::value_error("bad vrl trailer VEND"); + __if_hdr_unpack_${suffix}(packet_buff+2, if_packet_info, vrt_hdr_word32); if_packet_info.num_header_words32 += 2; //add vrl header if_packet_info.packet_count = (vrl_hdr >> 20) & 0xfff; break; @@ -375,16 +346,16 @@ void vrt::if_hdr_unpack_$(suffix)( } ######################################################################## -#end def +</%def> ######################################################################## -$gen_code("BE_MACRO", "be") -$gen_code("LE_MACRO", "le") +${gen_code("BE_MACRO", "be")} +${gen_code("LE_MACRO", "le")} """ def parse_tmpl(_tmpl_text, **kwargs): - from Cheetah.Template import Template - return str(Template(_tmpl_text, kwargs)) + from mako.template import Template + return Template(_tmpl_text).render(**kwargs) if __name__ == '__main__': import sys diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index ae33cc036..f92117a9e 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -71,7 +71,18 @@ private: timeval tv; tv.tv_sec = 0; tv.tv_usec = 100000; - libusb_handle_events_timeout(context, &tv); + int ret = libusb_handle_events_timeout(context, &tv); + switch (ret) + { + case LIBUSB_SUCCESS: + case LIBUSB_ERROR_TIMEOUT: + break; + case LIBUSB_ERROR_NO_DEVICE: + throw uhd::io_error(libusb_strerror(LIBUSB_ERROR_NO_DEVICE)); + default: + UHD_MSG(error) << __FUNCTION__ << ": " << libusb_strerror((libusb_error)ret) << std::endl; + break; + } } }; @@ -251,6 +262,21 @@ public: _claimed.push_back(interface); } + void clear_endpoints(unsigned char recv_endpoint, unsigned char send_endpoint) + { + int ret; + ret = libusb_clear_halt(this->get(), recv_endpoint | 0x80); + UHD_LOG << "usb device handle: recv endpoint clear: " << libusb_error_name(ret) << std::endl; + ret = libusb_clear_halt(this->get(), send_endpoint | 0x00); + UHD_LOG << "usb device handle: send endpoint clear: " << libusb_error_name(ret) << std::endl; + } + + void reset_device(void) + { + int ret = libusb_reset_device(this->get()); + UHD_LOG << "usb device handle: dev Reset: " << libusb_error_name(ret) << std::endl; + } + private: libusb::device::sptr _dev; //always keep a reference to device libusb_device_handle *_handle; @@ -345,15 +371,21 @@ libusb::special_handle::sptr libusb::special_handle::make(device::sptr dev){ std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list( boost::uint16_t vid, boost::uint16_t pid ){ - std::vector<usb_device_handle::sptr> handles; + return usb_device_handle::get_device_list(std::vector<usb_device_handle::vid_pid_pair_t>(1,usb_device_handle::vid_pid_pair_t(vid,pid))); +} +std::vector<usb_device_handle::sptr> usb_device_handle::get_device_list(const std::vector<usb_device_handle::vid_pid_pair_t>& vid_pid_pair_list) +{ + std::vector<usb_device_handle::sptr> handles; libusb::device_list::sptr dev_list = libusb::device_list::make(); - for (size_t i = 0; i < dev_list->size(); i++){ - usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); - if (handle->get_vendor_id() == vid and handle->get_product_id() == pid){ - handles.push_back(handle); - } + for(size_t iter = 0; iter < vid_pid_pair_list.size(); ++iter) + { + for (size_t i = 0; i < dev_list->size(); i++){ + usb_device_handle::sptr handle = libusb::special_handle::make(dev_list->at(i)); + if (handle->get_vendor_id() == vid_pid_pair_list[iter].first and handle->get_product_id() == vid_pid_pair_list[iter].second){ + handles.push_back(handle); + } + } } - return handles; } diff --git a/host/lib/transport/libusb1_base.hpp b/host/lib/transport/libusb1_base.hpp index b00946614..2ff1291d9 100644 --- a/host/lib/transport/libusb1_base.hpp +++ b/host/lib/transport/libusb1_base.hpp @@ -24,6 +24,29 @@ #include <uhd/transport/usb_device_handle.hpp> #include <libusb.h> +//! Define LIBUSB_CALL when its missing (non-windows) +#ifndef LIBUSB_CALL + #define LIBUSB_CALL +#endif /*LIBUSB_CALL*/ + +//! libusb_handle_events_timeout_completed is only in newer API +#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED + #define libusb_handle_events_timeout_completed(ctx, tx, completed) \ + libusb_handle_events_timeout(ctx, tx) +#endif /* HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED */ + +//! libusb_error_name is only in newer API +#ifndef HAVE_LIBUSB_ERROR_NAME + #define libusb_error_name(code) \ + str(boost::format("LIBUSB_ERROR_CODE %d") % code) +#endif /* HAVE_LIBUSB_ERROR_NAME */ + +//! libusb_strerror is only in newer API +#ifndef HAVE_LIBUSB_STRERROR + #define libusb_strerror(code) \ + libusb_error_name(code) +#endif /* HAVE_LIBUSB_STRERROR */ + /*********************************************************************** * Libusb object oriented smart pointer wrappers: * The following wrappers provide allocation and automatic deallocation @@ -135,6 +158,10 @@ namespace libusb { * Control interface: 0 */ virtual void claim_interface(int) = 0; + + virtual void clear_endpoints(unsigned char recv_endpoint, unsigned char send_endpoint) = 0; + + virtual void reset_device(void) = 0; }; /*! diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 1ac02d16f..b67b36d0a 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -43,23 +43,6 @@ using namespace uhd::transport; static const size_t DEFAULT_NUM_XFERS = 16; //num xfers static const size_t DEFAULT_XFER_SIZE = 32*512; //bytes -//! Define LIBUSB_CALL when its missing (non-windows) -#ifndef LIBUSB_CALL - #define LIBUSB_CALL -#endif /*LIBUSB_CALL*/ - -//! libusb_handle_events_timeout_completed is only in newer API -#ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED - #define libusb_handle_events_timeout_completed(ctx, tx, completed) \ - libusb_handle_events_timeout(ctx, tx) -#endif - -//! libusb_error_name is only in newer API -#ifndef HAVE_LIBUSB_ERROR_NAME - #define libusb_error_name(code) \ - str(boost::format("LIBUSB_ERROR_CODE %d") % code) -#endif - //! type for sharing the release queue with managed buffers class libusb_zero_copy_mb; typedef boost::shared_ptr<bounded_buffer<libusb_zero_copy_mb *> > mb_queue_sptr; @@ -155,7 +138,8 @@ public: result.is_recv = _is_recv; #endif const int ret = libusb_submit_transfer(_lut); - if (ret != 0) throw uhd::runtime_error(str(boost::format( + if (ret != LIBUSB_SUCCESS) + throw uhd::usb_error(ret, str(boost::format( "usb %s submit failed: %s") % _name % libusb_error_name(ret))); } @@ -164,8 +148,9 @@ public: { if (wait_for_completion(timeout)) { - if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format( - "usb %s transfer status: %d") % _name % int(result.status))); + if (result.status != LIBUSB_TRANSFER_COMPLETED) + throw uhd::runtime_error(str(boost::format("usb %s transfer status: %d") + % _name % libusb_error_name(result.status))); result.completed = 0; return make(reinterpret_cast<buffer_type *>(this), _lut->buffer, (_is_recv)? result.actual_length : _frame_size); } @@ -219,7 +204,8 @@ public: _num_frames(num_frames), _frame_size(frame_size), _buffer_pool(buffer_pool::make(_num_frames, _frame_size)), - _enqueued(_num_frames), _released(_num_frames) + _enqueued(_num_frames), _released(_num_frames), + _status(STATUS_RUNNING) { const bool is_recv = (endpoint & 0x80) != 0; const std::string name = str(boost::format("%s%d") % ((is_recv)? "rx" : "tx") % int(endpoint & 0x7f)); @@ -304,18 +290,24 @@ public: UHD_INLINE typename buffer_type::sptr get_buff(double timeout) { typename buffer_type::sptr buff; - libusb_zero_copy_mb *front = NULL; - boost::mutex::scoped_lock lock(_mutex); + + if (_status == STATUS_ERROR) + return buff; + + // Serialize access to buffers + boost::mutex::scoped_lock get_buff_lock(_get_buff_mutex); + + boost::mutex::scoped_lock queue_lock(_queue_mutex); if (_enqueued.empty()) { - _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6))); + _buff_ready_cond.timed_wait(queue_lock, boost::posix_time::microseconds(long(timeout*1e6))); } if (_enqueued.empty()) return buff; - front = _enqueued.front(); + libusb_zero_copy_mb *front = _enqueued.front(); - lock.unlock(); + queue_lock.unlock(); buff = front->get_new<buffer_type>(timeout); - lock.lock(); + queue_lock.lock(); if (buff) _enqueued.pop_front(); this->submit_what_we_can(); @@ -333,28 +325,39 @@ private: buffer_pool::sptr _buffer_pool; std::vector<boost::shared_ptr<libusb_zero_copy_mb> > _mb_pool; - boost::mutex _mutex; - boost::condition_variable _cond; + boost::mutex _queue_mutex; + boost::condition_variable _buff_ready_cond; + boost::mutex _get_buff_mutex; //! why 2 queues? there is room in the future to have > N buffers but only N in flight boost::circular_buffer<libusb_zero_copy_mb *> _enqueued, _released; + enum {STATUS_RUNNING, STATUS_ERROR} _status; + void enqueue_buffer(libusb_zero_copy_mb *mb) { - boost::mutex::scoped_lock l(_mutex); + boost::mutex::scoped_lock l(_queue_mutex); _released.push_back(mb); this->submit_what_we_can(); - l.unlock(); - _cond.notify_one(); + _buff_ready_cond.notify_one(); } void submit_what_we_can(void) { + if (_status == STATUS_ERROR) + return; while (not _released.empty() and not _enqueued.full()) { - _released.front()->submit(); - _enqueued.push_back(_released.front()); - _released.pop_front(); + try { + _released.front()->submit(); + _enqueued.push_back(_released.front()); + _released.pop_front(); + } + catch (uhd::usb_error& e) + { + _status = STATUS_ERROR; + throw e; + } } } diff --git a/host/lib/transport/nirio/lvbitx/process-lvbitx.py b/host/lib/transport/nirio/lvbitx/process-lvbitx.py index ab9625608..7887c3997 100755 --- a/host/lib/transport/nirio/lvbitx/process-lvbitx.py +++ b/host/lib/transport/nirio/lvbitx/process-lvbitx.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-2015 Ettus Research LLC # # 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 @@ -35,7 +35,7 @@ parser.add_option("--output-src-path", type="string", dest="output_src_path", he # Args if (len(args) < 1): - print 'ERROR: Please specify the input LVBITX file name' + print('ERROR: Please specify the input LVBITX file name') sys.exit(1) lvbitx_filename = args[0] @@ -44,16 +44,16 @@ autogen_src_path = os.path.abspath(options.output_src_path) if (options.output_s class_name = os.path.splitext(os.path.basename(input_filename))[0] if (not os.path.isfile(input_filename)): - print 'ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.' + print('ERROR: FPGA File ' + input_filename + ' could not be accessed or is not a file.') sys.exit(1) if (options.merge_bin is not None and not os.path.isfile(os.path.abspath(options.merge_bin))): - print 'ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.' + print('ERROR: FPGA Bin File ' + options.merge_bin + ' could not be accessed or is not a file.') sys.exit(1) if (not os.path.exists(autogen_src_path)): - print 'ERROR: Output path ' + autogen_src_path + ' could not be accessed.' + print('ERROR: Output path ' + autogen_src_path + ' could not be accessed.') sys.exit(1) if (options.output_lvbitx_path is not None and input_filename == os.path.join(autogen_src_path, class_name + '.lvbitx')): - print 'ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.' + print('ERROR: Input and output LVBITX files were the same. Choose a difference input file or output path.') sys.exit(1) # Get XML Tree Node @@ -122,7 +122,7 @@ def map_SubType_to_ScalarType(SubType): elif SubType == 'U64': ScalarType = 'RIO_SCALAR_TYPE_UQ' else: - print 'ERROR: No corresponding nirio_scalar_type_t value for SubType ' + SubType + ' .' + print('ERROR: No corresponding nirio_scalar_type_t value for SubType ' + SubType + ' .') sys.exit(1) return ScalarType; @@ -187,7 +187,7 @@ codegen_transform['lvbitx_signature'] = str.upper(root.find('SignatureRegister') # Write BIN file bitstream = base64.b64decode(root.find('Bitstream').text) if (options.output_lvbitx_path is not None and hashlib.md5(bitstream).hexdigest() != root.find('BitstreamMD5').text): - print 'ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.' + print('ERROR: The MD5 sum for the output LVBITX was incorrect. Make sure that the bitstream in the input LVBITX or BIN file is valid.') sys.exit(1) if (options.output_bin): fpga_bin_file = open(os.path.join(options.output_lvbitx_path, class_name + '.bin'), 'w') diff --git a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp index 4afc1579f..4d983de17 100644 --- a/host/lib/transport/nirio/nirio_driver_iface_linux.cpp +++ b/host/lib/transport/nirio/nirio_driver_iface_linux.cpp @@ -25,16 +25,6 @@ namespace nirio_driver_iface { -struct nirio_ioctl_block_t -{ - uint64_t in_buf; - uint64_t out_buf; - uint32_t in_buf_len; - uint32_t out_buf_len; - uint32_t bytes_returned; - uint32_t padding; -}; - nirio_status rio_open( const std::string& device_path, rio_dev_handle_t& device_handle) diff --git a/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp b/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp index f0ffe2ffc..444e98f1b 100644 --- a/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp +++ b/host/lib/transport/nirio/niriok_proxy_impl_v1.cpp @@ -26,8 +26,397 @@ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +// CTL_CODE macro for non-win OSes +#ifndef UHD_PLATFORM_WIN32 + #define CTL_CODE(a,controlCode,b,c) (controlCode) +#endif + +const uint32_t NIRIO_IOCTL_BASE = 0x800; + +const uint32_t NIRIO_IOCTL_SYNCOP = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 4, + METHOD_OUT_DIRECT, + FILE_READ_DATA | FILE_WRITE_DATA); + ///< The synchronous operation code. Note: We + /// must use METHOD_OUT_DIRECT on the syncOp() + /// IOCTL to ensure the contents of the output + /// block are available in the kernel. + +const uint32_t NIRIO_IOCTL_GET_IFACE_NUM = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 6, + METHOD_BUFFERED, + FILE_READ_DATA); ///< Get the interface number for a device + +const uint32_t NIRIO_IOCTL_GET_SESSION = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 8, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Gets a previously opened session to a device + +const uint32_t NIRIO_IOCTL_POST_OPEN = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 9, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called after opening a session + +const uint32_t NIRIO_IOCTL_PRE_CLOSE = + CTL_CODE(FILE_DEVICE_UNKNOWN, + NIRIO_IOCTL_BASE + 10, + METHOD_BUFFERED, + FILE_READ_ACCESS); ///< Called before closing a session + namespace uhd { namespace niusrprio { + // ------------------------------- + // Function Codes: defined as integers rather than enums because they + // are going to be carried accross boundaries so size matters + + struct NIRIO_FUNC + { + static const uint32_t GET32 = 0x00000001; + static const uint32_t SET32 = 0x00000002; + static const uint32_t SET_DRIVER_CONFIG = 0x00000007; + static const uint32_t FIFO = 0x00000008; + static const uint32_t IO = 0x0000000A; + static const uint32_t FIFO_STOP_ALL = 0x0000000C; + static const uint32_t ADD_RESOURCE = 0x0000000D; + static const uint32_t GET_STRING = 0x0000000E; + static const uint32_t SET_STRING = 0x0000000F; + static const uint32_t DOWNLOAD = 0x00000013; + static const uint32_t RESET = 0x00000014; + }; + + struct NIRIO_RESOURCE + { + static const uint32_t INPUT_FIFO = 0xD0000001; + static const uint32_t OUTPUT_FIFO = 0xD0000002; + }; + + struct NIRIO_FIFO + { + static const uint32_t CONFIGURE = 0x80000001; + static const uint32_t START = 0x80000002; + static const uint32_t STOP = 0x80000003; + static const uint32_t READ = 0x80000004; + static const uint32_t WRITE = 0x80000005; + static const uint32_t WAIT = 0x80000006; + static const uint32_t GRANT = 0x80000007; + }; + + struct NIRIO_IO + { + static const uint32_t POKE64 = 0xA0000005; + static const uint32_t POKE32 = 0xA0000006; + static const uint32_t POKE16 = 0xA0000007; + static const uint32_t POKE8 = 0xA0000008; + static const uint32_t PEEK64 = 0xA0000009; + static const uint32_t PEEK32 = 0xA000000A; + static const uint32_t PEEK16 = 0xA000000B; + static const uint32_t PEEK8 = 0xA000000C; + static const uint32_t READ_BLOCK = 0xA000000D; + static const uint32_t WRITE_BLOCK = 0xA000000E; + static const uint32_t GET_IO_WINDOW = 0xA000000F; + static const uint32_t GET_IO_WINDOW_SIZE = 0xA0000010; + }; + + struct nirio_ioctl_packet_t { + nirio_ioctl_packet_t(void* const _outBuf, const uint32_t _outSize, const int32_t _statusCode) + { + outBuf._64BitField = 0; + outBuf.pointer = _outBuf; + outSize = _outSize; + statusCode = _statusCode; + }; + + union { + void* pointer; + uint64_t _64BitField; + } outBuf; + + uint32_t outSize; + int32_t statusCode; + }; + + struct nirio_syncop_in_params_t + { + uint32_t function; + uint32_t subfunction; + + union + { + struct + { + uint32_t attribute; + uint32_t value; + } attribute32; + + struct + { + uint32_t attribute; + uint64_t value; + } attribute64; + + struct + { + uint32_t attribute; + } attributeStr; + + struct + { + uint32_t attribute; + } download; + + union + { + struct + { + uint32_t reserved_field_0_0_0; + } reserved_field_0_0; + struct + { + uint32_t reserved_field_0_1_0; + uint32_t reserved_field_0_1_1; + } reserved_field_0_1; + struct + { + uint32_t reserved_field_0_2_0; + } reserved_field_0_2; + } reserved_field_0; + + union + { + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + } fifo; + struct + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + uint32_t version; + uint32_t scalarType; + uint32_t bitWidth; + } fifoWithDataType; + struct + { + uint64_t rangeBaseAddress; + uint32_t rangeSizeInBytes; + uint32_t rangeAttribute; + } atomic; // obsolete + } add; + + struct + { + uint32_t channel; + + union + { + struct + { + uint32_t requestedDepth; + uint8_t requiresActuals; + } config; + struct + { + uint32_t timeout; + } read; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } readWithDataType; + struct + { + uint32_t timeout; + } write; + struct + { + uint32_t timeout; + uint32_t scalarType; + uint32_t bitWidth; + } writeWithDataType; + struct + { + uint32_t elementsRequested; + uint32_t scalarType; + uint32_t bitWidth; + uint32_t timeout; + uint8_t output; + } wait; + struct + { + uint32_t elements; + } grant; + } op; + } fifo; + + struct + { + uint64_t reserved_field_1_0; + uint32_t reserved_field_1_1; + uint32_t reserved_field_1_2; + } reserved_field_1; // Obsolete + + struct + { + uint32_t offset; + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + uint32_t sizeToMap; + } memoryMappedIoWindow; + } io; + + struct + { + uint32_t reserved_field_2_0; + uint32_t reserved_field_2_1; + } reserved_field_2; + + struct + { + uint32_t reserved_field_3_0; + } reserved_field_3; + + union + { + struct + { + uint32_t reserved_field_4_0; + int32_t reserved_field_4_1; + } wait; + } reserved_field_4; + + } params; + + uint32_t inbufByteLen; + + union + { + const void* pointer; + uint64_t _64BitField; + } inbuf; + }; + + static inline void init_syncop_in_params(nirio_syncop_in_params_t& param, const void* const buf, const uint32_t len) + { + param.inbuf._64BitField = 0; + param.inbuf.pointer = buf; + param.inbufByteLen = len; + } + + struct nirio_syncop_out_params_t + { + union + { + struct + { + uint32_t value; + } attribute32; + + struct + { + uint64_t value; + } attribute64; + + union + { + struct + { + uint32_t reserved_field_0_0; + } enable; + } reserved_field_0; + + struct + { + union + { + struct + { + uint32_t actualDepth; + uint32_t actualSize; + } config; + struct + { + uint32_t numberRead; + uint32_t numberRemaining; + } read; + struct + { + uint32_t numberRemaining; + } write; + struct + { + union + { + void* pointer; + uint64_t _64BitField; + } elements; + } wait; + } op; + } fifo; + + struct + { + union + { + union + { + uint64_t value64; + uint32_t value32; + uint16_t value16; + uint8_t value8; + } value; + union + { + void* memoryMappedAddress; + uint64_t _64BitField; + } memoryMappedIoWindow; + union + { + uint32_t size; + } memoryMappedIoWindowSize; + }; + } io; + + uint32_t stringLength; + + struct + { + uint32_t reserved_field_1_0; + } reserved_field_1; + + } params; + + uint32_t outbufByteLen; + + union + { + void* pointer; + uint64_t _64BitField; + } outbuf; + }; + + static inline void init_syncop_out_params(nirio_syncop_out_params_t& param, void* buf, uint32_t len) + { + param.outbuf._64BitField = 0; + param.outbuf.pointer = buf; + param.outbufByteLen = len; + } + //------------------------------------------------------- // niriok_proxy_impl_v1 //------------------------------------------------------- @@ -55,11 +444,11 @@ namespace uhd { namespace niusrprio interface_path, _device_handle), status); if (nirio_status_not_fatal(status)) { nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_POST_OPEN, + NIRIO_IOCTL_POST_OPEN, NULL, 0, NULL, 0), status); nirio_ioctl_packet_t out(&_interface_num, sizeof(_interface_num), 0); nirio_status_chain(nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_IFACE_NUM, + NIRIO_IOCTL_GET_IFACE_NUM, NULL, 0, &out, sizeof(out)), status); @@ -83,7 +472,7 @@ namespace uhd { namespace niusrprio if(nirio_driver_iface::rio_isopen(_device_handle)) { nirio_driver_iface::rio_ioctl( - _device_handle, nirio_driver_iface::NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); + _device_handle, NIRIO_IOCTL_PRE_CLOSE, NULL, 0, NULL, 0); nirio_driver_iface::rio_close(_device_handle); } } @@ -100,18 +489,6 @@ namespace uhd { namespace niusrprio return sync_operation(&in, sizeof(in), &out, sizeof(out)); } - nirio_status niriok_proxy_impl_v1::get_cached_session( - uint32_t& session) - { - READER_LOCK - - nirio_ioctl_packet_t out(&session, sizeof(session), 0); - return nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, - NULL, 0, - &out, sizeof(out)); - } - nirio_status niriok_proxy_impl_v1::get_version( nirio_version_t type, uint32_t& major, @@ -150,7 +527,7 @@ namespace uhd { namespace niusrprio nirio_ioctl_packet_t out(readBuffer, readBufferLength, 0); nirio_status ioctl_status = nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_SYNCOP, + NIRIO_IOCTL_SYNCOP, writeBuffer, writeBufferLength, &out, sizeof(out)); if (nirio_status_fatal(ioctl_status)) return ioctl_status; @@ -282,14 +659,14 @@ namespace uhd { namespace niusrprio nirio_status niriok_proxy_impl_v1::add_fifo_resource(const nirio_fifo_info_t& fifo_info) { - niriok_proxy_impl_v1::nirio_syncop_in_params_t in = {}; - niriok_proxy_impl_v1::nirio_syncop_out_params_t out = {}; + nirio_syncop_in_params_t in = {}; + nirio_syncop_out_params_t out = {}; - in.function = niriok_proxy_impl_v1::NIRIO_FUNC::ADD_RESOURCE; + in.function = NIRIO_FUNC::ADD_RESOURCE; if (fifo_info.direction == OUTPUT_FIFO) - in.subfunction = niriok_proxy_impl_v1::NIRIO_RESOURCE::OUTPUT_FIFO; + in.subfunction = NIRIO_RESOURCE::OUTPUT_FIFO; else - in.subfunction = niriok_proxy_impl_v1::NIRIO_RESOURCE::INPUT_FIFO; + in.subfunction = NIRIO_RESOURCE::INPUT_FIFO; in.params.add.fifoWithDataType.channel = fifo_info.channel; in.params.add.fifoWithDataType.baseAddress = fifo_info.base_addr; @@ -307,7 +684,7 @@ namespace uhd { namespace niusrprio nirio_syncop_in_params_t in = {}; nirio_syncop_out_params_t out = {}; - in.function = niriok_proxy_impl_v1::NIRIO_FUNC::SET_DRIVER_CONFIG; + in.function = NIRIO_FUNC::SET_DRIVER_CONFIG; in.subfunction = 0; return sync_operation(&in, sizeof(in), &out, sizeof(out)); diff --git a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp index 748db5cf8..f75de01e1 100644 --- a/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp +++ b/host/lib/transport/nirio/niriok_proxy_impl_v2.cpp @@ -27,9 +27,263 @@ #pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +#define IOCTL_TRANSPORT_GET32 IOCTL(0, 0, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_SET32 IOCTL(0, 1, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_GET_STRING IOCTL(0, 2, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_SET_STRING IOCTL(0, 3, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_RESET IOCTL(1, 1, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_ADD_INPUT_FIFO_RESOURCE IOCTL(2, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_ADD_OUTPUT_FIFO_RESOURCE IOCTL(2, 1, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_SET_DEVICE_CONFIG IOCTL(2, 3, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_FIFO_CONFIG IOCTL(4, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_START IOCTL(4, 1, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_STOP IOCTL(4, 2, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_READ IOCTL(4, 3, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_FIFO_WRITE IOCTL(4, 4, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_FIFO_WAIT IOCTL(4, 5, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_GRANT IOCTL(4, 6, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_FIFO_STOP_ALL IOCTL(4, 7, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_PEEK64 IOCTL(5, 2, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_PEEK32 IOCTL(5, 3, IOCTL_ACCESS_READ) +#define IOCTL_TRANSPORT_POKE64 IOCTL(5, 6, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_POKE32 IOCTL(5, 7, IOCTL_ACCESS_WRITE) +#define IOCTL_TRANSPORT_POST_OPEN IOCTL(8, 0, IOCTL_ACCESS_ANY) +#define IOCTL_TRANSPORT_PRE_CLOSE IOCTL(8, 1, IOCTL_ACCESS_ANY) + namespace uhd { namespace niusrprio { //------------------------------------------------------- + // ioctl param typedefs + //------------------------------------------------------- + typedef struct { + nirio_scalar_type_t scalarType; + nirio_u32_t bitWidth; + nirio_i32_t integerWordLength; + } nirio_fifo_data_type_t; + + typedef struct in_transport_get32 + { + nirio_device_attribute32_t attribute; + int32_t status; + } in_transport_get32_t; + typedef struct out_transport_get32 + { + uint32_t retVal__; + int32_t status; + } out_transport_get32_t; + typedef struct in_transport_set32 + { + nirio_device_attribute32_t attribute; + uint32_t value; + int32_t status; + } in_transport_set32_t; + typedef struct out_transport_set32 + { + int32_t status; + } out_transport_set32_t; + typedef struct out_transport_get_string + { + uint32_t stringLen; + int32_t status; + } out_transport_get_string_t; + typedef struct out_transport_set_string + { + int32_t status; + } out_transport_set_string_t; + typedef struct in_transport_reset + { + int32_t status; + } in_transport_reset_t; + typedef struct out_transport_reset + { + int32_t status; + } out_transport_reset_t; + typedef struct in_transport_add_input_fifo_resource + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + nirio_fifo_data_type_t dataType; + uint32_t version; + int32_t status; + } in_transport_add_input_fifo_resource_t; + typedef struct out_transport_addInputFifo_resource + { + int32_t status; + } out_transport_add_input_fifo_resource_t; + typedef struct in_transport_addOutputFifo_resource + { + uint32_t channel; + uint32_t baseAddress; + uint32_t depthInSamples; + nirio_fifo_data_type_t dataType; + uint32_t version; + int32_t status; + } in_transport_add_output_fifo_resource_t; + typedef struct out_transport_addOutputFifo_resource + { + int32_t status; + } out_transport_add_output_fifo_resource_t; + typedef struct in_transport_setDevice_config + { + uint32_t attribute; + int32_t status; + } in_transport_set_device_config_t; + typedef struct out_transport_setDevice_config + { + int32_t status; + } out_transport_set_device_config_t; + typedef struct in_transport_fifo_config + { + uint32_t channel; + aligned_uint64_t requestedDepth; + int32_t status; + } in_transport_fifo_config_t; + typedef struct out_transport_fifo_config + { + aligned_uint64_t actualDepth; + aligned_uint64_t actualSize; + int32_t status; + } out_transport_fifo_config_t; + typedef struct in_transport_fifo_start + { + uint32_t channel; + int32_t status; + } in_transport_fifo_start_t; + typedef struct out_transport_fifo_start + { + int32_t status; + } out_transport_fifo_start_t; + typedef struct in_transport_fifo_stop + { + uint32_t channel; + int32_t status; + } in_transport_fifo_stop_t; + typedef struct out_transport_fifo_stop + { + int32_t status; + } out_transport_fifo_stop_t; + typedef struct in_transport_fifo_read + { + uint32_t channel; + aligned_uint64_t buf; + uint32_t numberElements; + nirio_fifo_data_type_t dataType; + uint32_t timeout; + int32_t status; + } in_transport_fifo_read_t; + typedef struct out_transport_fifo_read + { + uint32_t read; + uint32_t remaining; + int32_t status; + } out_transport_fifo_read_t; + typedef struct in_transport_fifo_write + { + uint32_t channel; + aligned_uint64_t buf; + uint32_t numberElements; + nirio_fifo_data_type_t dataType; + uint32_t timeout; + int32_t status; + } in_transport_fifo_write_t; + typedef struct out_transport_fifo_write + { + uint32_t remaining; + int32_t status; + } out_transport_fifo_write_t; + typedef struct in_transport_fifo_wait + { + uint32_t channel; + aligned_uint64_t elementsRequested; + nirio_fifo_data_type_t dataType; + bool output; + uint32_t timeout; + int32_t status; + } in_transport_fifo_wait_t; + typedef struct out_transport_fifo_wait + { + aligned_uint64_t elements; + aligned_uint64_t elementsAcquired; + aligned_uint64_t elementsRemaining; + int32_t status; + } out_transport_fifo_wait_t; + typedef struct in_transport_fifo_grant + { + uint32_t channel; + aligned_uint64_t elements; + int32_t status; + } in_transport_fifo_grant_t; + typedef struct out_transport_fifo_grant + { + int32_t status; + } out_transport_fifo_grant_t; + typedef struct in_transport_fifoStop_all + { + int32_t status; + } in_transport_fifo_stop_all_t; + typedef struct out_transport_fifoStop_all + { + int32_t status; + } out_transport_fifo_stop_all_t; + typedef struct in_transport_peek64 + { + uint32_t offset; + int32_t status; + } in_transport_peek64_t; + typedef struct out_transport_peek64 + { + aligned_uint64_t retVal__; + int32_t status; + } out_transport_peek64_t; + typedef struct in_transport_peek32 + { + uint32_t offset; + int32_t status; + } in_transport_peek32_t; + typedef struct out_transport_peek32 + { + uint32_t retVal__; + int32_t status; + } out_transport_peek32_t; + typedef struct in_transport_poke64 + { + uint32_t offset; + aligned_uint64_t value; + int32_t status; + } in_transport_poke64_t; + typedef struct out_transport_poke64 + { + int32_t status; + } out_transport_poke64_t; + typedef struct in_transport_poke32 + { + uint32_t offset; + uint32_t value; + int32_t status; + } in_transport_poke32_t; + typedef struct out_transport_poke32 + { + int32_t status; + } out_transport_poke32_t; + typedef struct in_transport_post_open + { + int32_t status; + } in_transport_post_open_t; + typedef struct out_transport_post_open + { + int32_t status; + } out_transport_post_open_t; + typedef struct in_transport_pre_close + { + int32_t status; + } in_transport_pre_close_t; + typedef struct out_transport_pre_close + { + int32_t status; + } out_transport_pre_close_t; + + //------------------------------------------------------- // niriok_proxy_impl_v2 //------------------------------------------------------- niriok_proxy_impl_v2::niriok_proxy_impl_v2() @@ -110,18 +364,6 @@ namespace uhd { namespace niusrprio return out.status; } - nirio_status niriok_proxy_impl_v2::get_cached_session( - uint32_t& session) - { - READER_LOCK - - nirio_ioctl_packet_t out(&session, sizeof(session), 0); - return nirio_driver_iface::rio_ioctl(_device_handle, - nirio_driver_iface::NIRIO_IOCTL_GET_SESSION, - NULL, 0, - &out, sizeof(out)); - } - nirio_status niriok_proxy_impl_v2::get_version( nirio_version_t type, uint32_t& major, @@ -580,7 +822,7 @@ namespace uhd { namespace niusrprio out_transport_fifo_read_t out = {}; in.channel = channel; - in.buf = reinterpret_cast<tAlignedU64>(buffer); + in.buf = reinterpret_cast<aligned_uint64_t>(buffer); in.numberElements = elements_to_read; in.dataType.scalarType = map_int_to_scalar_type(scalar_type); in.dataType.bitWidth = bit_width; @@ -620,7 +862,7 @@ namespace uhd { namespace niusrprio out_transport_fifo_write_t out = {}; in.channel = channel; - in.buf = reinterpret_cast<tAlignedU64>(buffer); + in.buf = reinterpret_cast<aligned_uint64_t>(buffer); in.numberElements = elements_to_write; in.dataType.scalarType = map_int_to_scalar_type(scalar_type); in.dataType.bitWidth = bit_width; diff --git a/host/lib/transport/nirio/rpc/rpc_client.cpp b/host/lib/transport/nirio/rpc/rpc_client.cpp index cf8e9c1a9..bbaf9f235 100644 --- a/host/lib/transport/nirio/rpc/rpc_client.cpp +++ b/host/lib/transport/nirio/rpc/rpc_client.cpp @@ -52,7 +52,7 @@ rpc_client::rpc_client ( //- address_configured: Only return addresses if a non-loopback address is configured for the system. //- numeric_host: No name resolution should be attempted for host //- numeric_service: No name resolution should be attempted for service - tcp::resolver::query::flags query_flags; + tcp::resolver::query::flags query_flags(tcp::resolver::query::passive); tcp::resolver::query query(tcp::v4(), server, port, query_flags); tcp::resolver::iterator iterator = resolver.resolve(query); diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 5c84327a4..0f1f7ff3a 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -147,7 +147,7 @@ public: */ void set_xport_chan_get_buff(const size_t xport_chan, const get_buff_type &get_buff, const bool flush = false){ if (flush){ - while (get_buff(0.0)); + while (get_buff(0.0)) {}; } _props.at(xport_chan).get_buff = get_buff; } @@ -560,6 +560,11 @@ private: _props[index].handle_overflow(); curr_info.metadata = metadata; UHD_MSG(fastpath) << "O"; + + // Not sending flow control would cause timeouts due to source flow control locking up + if(_props[index].handle_flowctrl) { + _props[index].handle_flowctrl(next_info[index].ifpi.packet_count); + } } return; diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index adc7d5585..70fb5b552 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -87,7 +87,10 @@ public: if (wait_for_recv_ready(_sock_fd, timeout)){ _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); - UHD_ASSERT_THROW(_len > 0); // TODO: Handle case of recv error + if (_len == 0) + throw uhd::io_error("socket closed"); + if (_len < 0) + throw uhd::io_error(str(boost::format("recv error on socket: %s") % strerror(errno))); index++; //advances the caller's buffer return make(this, _mem, size_t(_len)); } @@ -126,6 +129,10 @@ public: boost::this_thread::sleep(boost::posix_time::microseconds(1)); continue; //try to send again } + if (ret == -1) + { + throw uhd::io_error(str(boost::format("send error on socket: %s") % strerror(errno))); + } UHD_ASSERT_THROW(ret == ssize_t(size())); } _claimer.release(); diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index f19043c1e..ebb788183 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2013 Ettus Research LLC +# Copyright 2011-2013,2015 Ettus Research LLC # # 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 @@ -86,8 +86,22 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/ranges.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sensors.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serial.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sid.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/filters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/byte_vector.cpp ) + +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/metadata_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ranges_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/sensors_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/string_vector_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tune_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_info_c.cpp + ) +ENDIF() diff --git a/host/lib/types/byte_vector.cpp b/host/lib/types/byte_vector.cpp new file mode 100644 index 000000000..071cdb8cb --- /dev/null +++ b/host/lib/types/byte_vector.cpp @@ -0,0 +1,42 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/foreach.hpp> + +#include <uhd/types/byte_vector.hpp> + +namespace uhd{ + +std::string bytes_to_string(const byte_vector_t &bytes){ + std::string out; + BOOST_FOREACH(boost::uint8_t byte, bytes){ + if (byte < 32 or byte > 127) return out; + out += byte; + } + return out; +} + +byte_vector_t string_to_bytes(const std::string &str, size_t max_length){ + byte_vector_t bytes; + for (size_t i = 0; i < std::min(str.size(), max_length); i++){ + bytes.push_back(str[i]); + } + if (bytes.size() < max_length - 1) bytes.push_back('\0'); + return bytes; +} + +} /* namespace uhd */ diff --git a/host/lib/types/filters.cpp b/host/lib/types/filters.cpp new file mode 100644 index 000000000..4ee06491f --- /dev/null +++ b/host/lib/types/filters.cpp @@ -0,0 +1,74 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/filters.hpp> + +using namespace uhd; + +std::ostream& uhd::operator<<(std::ostream& os, filter_info_base& f) +{ + return os << f.to_pp_string(); +} + +std::string filter_info_base::to_pp_string() +{ + std::ostringstream os; + os << "[filter_info_base]" << std::endl; + switch(_type){ + case ANALOG_LOW_PASS: + os << "type: " << "Analog Low-pass" << std::endl; + break; + case ANALOG_BAND_PASS: + os << "type: " << "Analog Band-pass" << std::endl; + break; + case DIGITAL_I16: + os << "type: " << "Digital (i16)" << std::endl; + break; + case DIGITAL_FIR_I16: + os << "type: " << "Digital FIR (i16)" << std::endl; + break; + default: + os << "type: " << "Unknown type!" << std::endl; + break; + } + + os << "bypass enable: " << _bypass << std::endl + <<"position index: " << _position_index << std::endl; + + std::string str = os.str(); + return str; +} + +std::string analog_filter_base::to_pp_string() +{ + std::ostringstream os; + os << filter_info_base::to_pp_string() << + "\t[analog_filter_base]" << std::endl << + "\tdesc: " << _analog_type << std::endl; + return std::string(os.str()); + +} + +std::string analog_filter_lp::to_pp_string() +{ + std::ostringstream os; + os << analog_filter_base::to_pp_string() << + "\t\t[analog_filter_lp]" << std::endl << + "\t\tcutoff: " << _cutoff << std::endl << + "\t\trolloff: " << _rolloff << std::endl; + return std::string(os.str()); +} diff --git a/host/lib/types/metadata_c.cpp b/host/lib/types/metadata_c.cpp new file mode 100644 index 000000000..96f43d140 --- /dev/null +++ b/host/lib/types/metadata_c.cpp @@ -0,0 +1,315 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/metadata.h> + +#include <uhd/types/time_spec.hpp> + +#include <string.h> + +/* + * RX metadata + */ + +uhd_error uhd_rx_metadata_make( + uhd_rx_metadata_handle* handle +){ + UHD_SAFE_C( + *handle = new uhd_rx_metadata_t; + ) +} + +uhd_error uhd_rx_metadata_free( + uhd_rx_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_rx_metadata_has_time_spec( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_rx_metadata_time_spec( + uhd_rx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->rx_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_rx_metadata_more_fragments( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.more_fragments; + ) +} + +uhd_error uhd_rx_metadata_fragment_offset( + uhd_rx_metadata_handle h, + size_t *fragment_offset_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *fragment_offset_out = h->rx_metadata_cpp.fragment_offset; + ) +} + +uhd_error uhd_rx_metadata_start_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.start_of_burst; + ) +} + +uhd_error uhd_rx_metadata_end_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.end_of_burst; + ) +} + +uhd_error uhd_rx_metadata_out_of_sequence( + uhd_rx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->rx_metadata_cpp.out_of_sequence; + ) +} + +uhd_error uhd_rx_metadata_to_pp_string( + uhd_rx_metadata_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->rx_metadata_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_rx_metadata_error_code( + uhd_rx_metadata_handle h, + uhd_rx_metadata_error_code_t *error_code_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *error_code_out = uhd_rx_metadata_error_code_t(h->rx_metadata_cpp.error_code); + ) +} + +uhd_error uhd_rx_metadata_strerror( + uhd_rx_metadata_handle h, + char* strerror_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string strerror_cpp = h->rx_metadata_cpp.strerror(); + memset(strerror_out, '\0', strbuffer_len); + strncpy(strerror_out, strerror_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_rx_metadata_last_error( + uhd_rx_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/* + * TX metadata + */ + +uhd_error uhd_tx_metadata_make( + uhd_tx_metadata_handle* handle, + bool has_time_spec, + time_t full_secs, + double frac_secs, + bool start_of_burst, + bool end_of_burst +){ + UHD_SAFE_C( + *handle = new uhd_tx_metadata_t; + (*handle)->tx_metadata_cpp.has_time_spec = has_time_spec; + if(has_time_spec){ + (*handle)->tx_metadata_cpp.time_spec = uhd::time_spec_t(full_secs, frac_secs); + } + (*handle)->tx_metadata_cpp.start_of_burst = start_of_burst; + (*handle)->tx_metadata_cpp.end_of_burst = end_of_burst; + ) +} + +uhd_error uhd_tx_metadata_free( + uhd_tx_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_tx_metadata_has_time_spec( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_tx_metadata_time_spec( + uhd_tx_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->tx_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_tx_metadata_start_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.start_of_burst; + ) +} + +uhd_error uhd_tx_metadata_end_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->tx_metadata_cpp.end_of_burst; + ) +} + +uhd_error uhd_tx_metadata_last_error( + uhd_tx_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/* + * Async metadata + */ + +uhd_error uhd_async_metadata_make( + uhd_async_metadata_handle* handle +){ + UHD_SAFE_C( + *handle = new uhd_async_metadata_t; + ) +} + +uhd_error uhd_async_metadata_free( + uhd_async_metadata_handle* handle +){ + UHD_SAFE_C( + delete *handle; + *handle = NULL; + ) +} + +uhd_error uhd_async_metadata_channel( + uhd_async_metadata_handle h, + size_t *channel_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *channel_out = h->async_metadata_cpp.channel; + ) +} + +uhd_error uhd_async_metadata_has_time_spec( + uhd_async_metadata_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->async_metadata_cpp.has_time_spec; + ) +} + +uhd_error uhd_async_metadata_time_spec( + uhd_async_metadata_handle h, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = h->async_metadata_cpp.time_spec; + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_async_metadata_event_code( + uhd_async_metadata_handle h, + uhd_async_metadata_event_code_t *event_code_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *event_code_out = uhd_async_metadata_event_code_t(h->async_metadata_cpp.event_code); + ) +} + +uhd_error uhd_async_metadata_user_payload( + uhd_async_metadata_handle h, + uint32_t user_payload_out[4] +){ + UHD_SAFE_C_SAVE_ERROR(h, + memcpy(user_payload_out, h->async_metadata_cpp.user_payload, 4*sizeof(uint32_t)); + ) +} + +uhd_error uhd_async_metadata_last_error( + uhd_async_metadata_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/ranges_c.cpp b/host/lib/types/ranges_c.cpp new file mode 100644 index 000000000..0c0df24ce --- /dev/null +++ b/host/lib/types/ranges_c.cpp @@ -0,0 +1,162 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/ranges.h> + +#include <string.h> + +/* + * uhd::range_t + */ +uhd_error uhd_range_to_pp_string( + const uhd_range_t *range, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + uhd::range_t range_cpp = uhd_range_c_to_cpp(range); + std::string pp_string_cpp = range_cpp.to_pp_string(); + + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd::range_t uhd_range_c_to_cpp( + const uhd_range_t *range_c +){ + return uhd::range_t(range_c->start, range_c->stop, range_c->step); +} + +void uhd_range_cpp_to_c( + const uhd::range_t &range_cpp, + uhd_range_t *range_c +){ + range_c->start = range_cpp.start(); + range_c->stop = range_cpp.stop(); + range_c->step = range_cpp.step(); +} + +/* + * uhd::meta_range_t + */ +uhd_error uhd_meta_range_make( + uhd_meta_range_handle* h +){ + UHD_SAFE_C( + (*h) = new uhd_meta_range_t; + ) +} + +uhd_error uhd_meta_range_free( + uhd_meta_range_handle* h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_meta_range_start( + uhd_meta_range_handle h, + double *start_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *start_out = h->meta_range_cpp.start(); + ) +} + +uhd_error uhd_meta_range_stop( + uhd_meta_range_handle h, + double *stop_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *stop_out = h->meta_range_cpp.stop(); + ) +} + +uhd_error uhd_meta_range_step( + uhd_meta_range_handle h, + double *step_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *step_out = h->meta_range_cpp.step(); + ) +} + +uhd_error uhd_meta_range_clip( + uhd_meta_range_handle h, + double value, + bool clip_step, + double *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = h->meta_range_cpp.clip(value, clip_step); + ) +} + +uhd_error uhd_meta_range_size( + uhd_meta_range_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->meta_range_cpp.size(); + ) +} + +uhd_error uhd_meta_range_push_back( + uhd_meta_range_handle h, + const uhd_range_t *range +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->meta_range_cpp.push_back(uhd_range_c_to_cpp(range)); + ) +} + +uhd_error uhd_meta_range_at( + uhd_meta_range_handle h, + size_t num, + uhd_range_t *range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd_range_cpp_to_c(h->meta_range_cpp.at(num), + range_out); + ) +} + +uhd_error uhd_meta_range_to_pp_string( + uhd_meta_range_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->meta_range_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_meta_range_last_error( + uhd_meta_range_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/sensors_c.cpp b/host/lib/types/sensors_c.cpp new file mode 100644 index 000000000..f1976c102 --- /dev/null +++ b/host/lib/types/sensors_c.cpp @@ -0,0 +1,228 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/sensors.h> + +#include <boost/lexical_cast.hpp> + +#include <stdexcept> +#include <string.h> +#include <string> + +uhd_error uhd_sensor_value_make_from_bool( + uhd_sensor_value_handle* h, + const char* name, + bool value, + const char* utrue, + const char* ufalse +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + utrue, + ufalse); + ) +} + +uhd_error uhd_sensor_value_make_from_int( + uhd_sensor_value_handle* h, + const char* name, + int value, + const char* unit, + const char* formatter +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + std::string fmt(formatter); + if(fmt.empty()){ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + } + else{ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit, + fmt); + } + ) +} + +uhd_error uhd_sensor_value_make_from_realnum( + uhd_sensor_value_handle* h, + const char* name, + double value, + const char* unit, + const char* formatter +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + std::string fmt(formatter); + if(fmt.empty()){ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + } + else{ + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit, + fmt); + } + ) +} + +uhd_error uhd_sensor_value_make_from_string( + uhd_sensor_value_handle* h, + const char* name, + const char* value, + const char* unit +){ + try{ + *h = new uhd_sensor_value_t; + } + catch(...){ + return UHD_ERROR_UNKNOWN; + } + + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->sensor_value_cpp = new uhd::sensor_value_t(name, + value, + unit); + ) +} + +uhd_error uhd_sensor_value_free( + uhd_sensor_value_handle *h +){ + UHD_SAFE_C( + delete (*h)->sensor_value_cpp; + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_sensor_value_to_bool( + uhd_sensor_value_handle h, + bool *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_bool(); + ) +} + +uhd_error uhd_sensor_value_to_int( + uhd_sensor_value_handle h, + int *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_int(); + ) +} + +uhd_error uhd_sensor_value_to_realnum( + uhd_sensor_value_handle h, + double *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = h->sensor_value_cpp->to_real(); + ) +} + +uhd_error uhd_sensor_value_name( + uhd_sensor_value_handle h, + char* name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(name_out, '\0', strbuffer_len); + strncpy(name_out, h->sensor_value_cpp->name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_value( + uhd_sensor_value_handle h, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(value_out, '\0', strbuffer_len); + strncpy(value_out, h->sensor_value_cpp->value.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_unit( + uhd_sensor_value_handle h, + char* unit_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(unit_out, '\0', strbuffer_len); + strncpy(unit_out, h->sensor_value_cpp->unit.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_data_type( + uhd_sensor_value_handle h, + uhd_sensor_value_data_type_t *data_type_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *data_type_out = uhd_sensor_value_data_type_t(h->sensor_value_cpp->type); + ) +} + +uhd_error uhd_sensor_value_to_pp_string( + uhd_sensor_value_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->sensor_value_cpp->to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_sensor_value_last_error( + uhd_sensor_value_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/sid.cpp b/host/lib/types/sid.cpp new file mode 100644 index 000000000..2fc3781cf --- /dev/null +++ b/host/lib/types/sid.cpp @@ -0,0 +1,153 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/format.hpp> +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/utils/cast.hpp> + +using namespace uhd; + +sid_t::sid_t() + : _sid(0x0000), _set(false) +{ +} + +sid_t::sid_t(boost::uint32_t sid) + : _sid(sid), _set(true) +{ +} + +sid_t::sid_t(boost::uint8_t src_addr, boost::uint8_t src_ep, boost::uint8_t dst_addr, boost::uint8_t dst_ep) + : _sid(0x0000), _set(true) +{ + set_src_addr(src_addr); + set_src_endpoint(src_ep); + set_dst_addr(dst_addr); + set_dst_endpoint(dst_ep); +} + +sid_t::sid_t(const std::string &sid_str) + : _sid(0x0000), _set(false) +{ + set_from_str(sid_str); +} + +std::string sid_t::to_pp_string() const +{ + if (not _set) { + return "x.x>x.x"; + } + return str(boost::format("%d.%d>%d.%d") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + +std::string sid_t::to_pp_string_hex() const +{ + if (not _set) { + return "xx:xx>xx:xx"; + } + return str(boost::format("%02x:%02x>%02x:%02x") + % get_src_addr() + % get_src_endpoint() + % get_dst_addr() + % get_dst_endpoint() + ); +} + + +void sid_t::set_sid(boost::uint32_t new_sid) +{ + _set = true; + _sid = new_sid; +} + +void sid_t::set_from_str(const std::string &sid_str) +{ + const std::string dec_regex = "(\\d{1,3})\\.(\\d{1,3})[.:/><](\\d{1,3})\\.(\\d{1,3})"; + const std::string hex_regex = "([[:xdigit:]]{2}):([[:xdigit:]]{2})[.:/><]([[:xdigit:]]{2}):([[:xdigit:]]{2})"; + + boost::cmatch matches; + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(dec_regex))) { + set_src_addr(boost::lexical_cast<size_t>(matches[1])); + set_src_endpoint(boost::lexical_cast<size_t>(matches[2])); + set_dst_addr(boost::lexical_cast<size_t>(matches[3])); + set_dst_endpoint(boost::lexical_cast<size_t>(matches[4])); + return; + } + + if (boost::regex_match(sid_str.c_str(), matches, boost::regex(hex_regex))) { + set_src_addr(uhd::cast::hexstr_cast<size_t>(matches[1])); + set_src_endpoint(uhd::cast::hexstr_cast<size_t>(matches[2])); + set_dst_addr(uhd::cast::hexstr_cast<size_t>(matches[3])); + set_dst_endpoint(uhd::cast::hexstr_cast<size_t>(matches[4])); + return; + } + + throw uhd::value_error(str(boost::format("Invalid SID representation: %s") % sid_str)); +} + +void sid_t::set_src(boost::uint32_t new_addr) { + set_sid((_sid & 0x0000FFFF) | ((new_addr & 0xFFFF) << 16)); +} + +void sid_t::set_dst(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF0000) | (new_addr & 0xFFFF)); +} + +void sid_t::set_src_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0x00FFFFFF) | ((new_addr & 0xFF) << 24)); +} + +void sid_t::set_src_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFF00FFFF) | ((new_addr & 0xFF) << 16)); +} + +void sid_t::set_dst_addr(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFF00FF) | ((new_addr & 0xFF) << 8)); +} + +void sid_t::set_dst_endpoint(boost::uint32_t new_addr) { + set_sid((_sid & 0xFFFFFF00) | ((new_addr & 0xFF) << 0)); +} + +void sid_t::set_dst_xbarport(boost::uint32_t new_xbarport) +{ + set_sid((_sid & 0xFFFFFF0F) | ((new_xbarport & 0xF) << 4)); +} + +void sid_t::set_dst_blockport(boost::uint32_t new_blockport) +{ + set_sid((_sid & 0xFFFFFFF0) | ((new_blockport & 0xF) << 0)); +} + +sid_t sid_t::reversed() +{ + return sid_t((get_dst() << 16) | get_src()); +} + +void sid_t::reverse() +{ + set_sid((get_dst() << 16) | get_src()); +} + diff --git a/host/lib/types/string_vector_c.cpp b/host/lib/types/string_vector_c.cpp new file mode 100644 index 000000000..b50c7cdff --- /dev/null +++ b/host/lib/types/string_vector_c.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/string_vector.h> + +#include <string.h> + +uhd_error uhd_string_vector_make( + uhd_string_vector_handle *h +){ + UHD_SAFE_C( + (*h) = new uhd_string_vector_t; + ) +} + +uhd_error uhd_string_vector_free( + uhd_string_vector_handle *h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_string_vector_push_back( + uhd_string_vector_handle *h, + const char* value +){ + UHD_SAFE_C_SAVE_ERROR((*h), + (*h)->string_vector_cpp.push_back(value); + ) +} + +uhd_error uhd_string_vector_at( + uhd_string_vector_handle h, + size_t index, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(value_out, '\0', strbuffer_len); + + const std::string& value_cpp = h->string_vector_cpp.at(index); + strncpy(value_out, value_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_string_vector_size( + uhd_string_vector_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->string_vector_cpp.size(); + ) +} + +uhd_error uhd_string_vector_last_error( + uhd_string_vector_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/types/tune_c.cpp b/host/lib/types/tune_c.cpp new file mode 100644 index 000000000..c62935cb8 --- /dev/null +++ b/host/lib/types/tune_c.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/tune_request.h> +#include <uhd/types/tune_result.h> + +#include <boost/format.hpp> + +#include <cstdlib> +#include <cstring> +#include <iostream> + +/* + * Tune request + */ + +uhd::tune_request_t uhd_tune_request_c_to_cpp(uhd_tune_request_t *tune_request_c){ + uhd::tune_request_t tune_request_cpp; + + tune_request_cpp.target_freq = tune_request_c->target_freq; + tune_request_cpp.rf_freq_policy = uhd::tune_request_t::policy_t(tune_request_c->rf_freq_policy); + tune_request_cpp.rf_freq = tune_request_c->rf_freq; + tune_request_cpp.dsp_freq_policy = uhd::tune_request_t::policy_t(tune_request_c->dsp_freq_policy); + tune_request_cpp.dsp_freq = tune_request_c->dsp_freq; + + std::string args_cpp = (tune_request_c->args) ? tune_request_c->args : std::string(""); + tune_request_cpp.args = uhd::device_addr_t(args_cpp); + + return tune_request_cpp; +} + +/* + * Tune result + */ + +void uhd_tune_result_to_pp_string(uhd_tune_result_t *tune_result_c, + char* pp_string_out, size_t strbuffer_len){ + std::string pp_string_cpp = uhd_tune_result_c_to_cpp(tune_result_c).to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); +} + +uhd::tune_result_t uhd_tune_result_c_to_cpp(uhd_tune_result_t *tune_result_c){ + uhd::tune_result_t tune_result_cpp; + + tune_result_cpp.clipped_rf_freq = tune_result_c->clipped_rf_freq; + tune_result_cpp.target_rf_freq = tune_result_c->target_rf_freq; + tune_result_cpp.actual_rf_freq = tune_result_c->actual_rf_freq; + tune_result_cpp.target_dsp_freq = tune_result_c->target_dsp_freq; + tune_result_cpp.actual_dsp_freq = tune_result_c->actual_dsp_freq; + + return tune_result_cpp; +} + +void uhd_tune_result_cpp_to_c(const uhd::tune_result_t &tune_result_cpp, + uhd_tune_result_t *tune_result_c){ + tune_result_c->clipped_rf_freq = tune_result_cpp.clipped_rf_freq; + tune_result_c->target_rf_freq = tune_result_cpp.target_rf_freq; + tune_result_c->actual_rf_freq = tune_result_cpp.actual_rf_freq; + tune_result_c->target_dsp_freq = tune_result_cpp.target_dsp_freq; + tune_result_c->actual_dsp_freq = tune_result_cpp.actual_dsp_freq; +} diff --git a/host/lib/types/usrp_info_c.cpp b/host/lib/types/usrp_info_c.cpp new file mode 100644 index 000000000..77354d901 --- /dev/null +++ b/host/lib/types/usrp_info_c.cpp @@ -0,0 +1,44 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types/usrp_info.h> + +uhd_error uhd_usrp_rx_info_free(uhd_usrp_rx_info_t *rx_info){ + free(rx_info->mboard_id); + free(rx_info->mboard_name); + free(rx_info->mboard_serial); + free(rx_info->rx_id); + free(rx_info->rx_subdev_name); + free(rx_info->rx_subdev_spec); + free(rx_info->rx_serial); + free(rx_info->rx_antenna); + + return UHD_ERROR_NONE; +} + +uhd_error uhd_usrp_tx_info_free(uhd_usrp_tx_info_t *tx_info){ + free(tx_info->mboard_id); + free(tx_info->mboard_name); + free(tx_info->mboard_serial); + free(tx_info->tx_id); + free(tx_info->tx_subdev_name); + free(tx_info->tx_subdev_spec); + free(tx_info->tx_serial); + free(tx_info->tx_antenna); + + return UHD_ERROR_NONE; +} diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f6788b5ef..5c9592970 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2013 Ettus Research LLC +# Copyright 2010-2015 Ettus Research LLC # # 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 @@ -18,6 +18,10 @@ ######################################################################## # This file included, use CMake directory variables ######################################################################## +find_package(GPSD) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/dboard_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom.cpp @@ -30,6 +34,24 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_c.cpp + ) +ENDIF(ENABLE_C_API) + +LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) + +IF(ENABLE_GPSD) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp + ) + LIBUHD_APPEND_LIBS(${LIBGPS_LIBRARY}) +ENDIF(ENABLE_GPSD) + INCLUDE_SUBDIRECTORY(cores) INCLUDE_SUBDIRECTORY(dboard) INCLUDE_SUBDIRECTORY(common) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index bcc5ac74d..1558cd974 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2013 Ettus Research LLC +# Copyright 2011-2013,2015 Ettus Research LLC # # 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the B100 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_B100) LIBUHD_APPEND_SOURCES( diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index ce89b5d80..76710dc65 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2012-2013 Ettus Research LLC +# Copyright 2012-2013,2015 Ettus Research LLC # # 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 @@ -22,10 +22,11 @@ ######################################################################## # Conditionally configure the B200 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_B200) LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 270d3bb4b..4754a6357 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -511,7 +511,7 @@ public: throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); } - boost::uint32_t load_fpga(const std::string filestring) { + boost::uint32_t load_fpga(const std::string filestring, bool force) { boost::uint8_t fx3_state = 0; boost::uint32_t wait_count; @@ -522,7 +522,7 @@ public: hash_type hash = generate_hash(filename); hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); - if (hash == loaded_hash) return 0; + if (hash == loaded_hash and !force) return 0; // Establish default largest possible control request transfer size based on operating USB speed int transfer_size = VREQ_DEFAULT_SIZE; diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 1d123439a..19ec561fa 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013,2015 Ettus Research LLC +// Copyright 2012-2013 Ettus Research LLC // // 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 @@ -27,40 +27,55 @@ #include <boost/utility.hpp> #include "ad9361_ctrl.hpp" -enum b200_type_t { +enum b200_product_t { B200, - B210 + B210, + B205 }; +// These are actual USB PIDs (not Ettus Product IDs) const static boost::uint16_t B200_VENDOR_ID = 0x2500; const static boost::uint16_t B200_VENDOR_NI_ID = 0x3923; const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t B205_PRODUCT_ID = 0x0021; const static boost::uint16_t B200_PRODUCT_NI_ID = 0x7813; const static boost::uint16_t B210_PRODUCT_NI_ID = 0x7814; const static boost::uint16_t FX3_VID = 0x04b4; const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; const static boost::uint16_t FX3_REENUM_PID = 0x00f0; +//! Map the USB PID to the product (only for PIDs that map to a single product) +static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PID_TO_PRODUCT = boost::assign::map_list_of + (B200_PRODUCT_NI_ID, B200) + (B210_PRODUCT_NI_ID, B210) + (B205_PRODUCT_ID, B205) +; + static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; -//! Map the product ID (in the EEPROM) to a device type -static const uhd::dict<boost::uint16_t, b200_type_t> B2X0_PRODUCT_ID = boost::assign::map_list_of +//! Map the EEPROM product ID codes to the product +static const uhd::dict<boost::uint16_t, b200_product_t> B2XX_PRODUCT_ID = boost::assign::map_list_of (0x0001, B200) (0x7737, B200) (B200_PRODUCT_NI_ID, B200) (0x0002, B210) (0x7738, B210) (B210_PRODUCT_NI_ID, B210) + (0x0003, B205) + (0x7739, B205) ; -static const uhd::dict<b200_type_t, std::string> B2X0_STR_NAMES = boost::assign::map_list_of + +static const uhd::dict<b200_product_t, std::string> B2XX_STR_NAMES = boost::assign::map_list_of (B200, "B200") (B210, "B210") + (B205, "B200mini") ; -static const uhd::dict<b200_type_t, std::string> B2X0_FPGA_FILE_NAME = boost::assign::map_list_of +static const uhd::dict<b200_product_t, std::string> B2XX_FPGA_FILE_NAME = boost::assign::map_list_of (B200, "usrp_b200_fpga.bin") (B210, "usrp_b210_fpga.bin") + (B205, "usrp_b200mini_fpga.bin") ; @@ -97,7 +112,7 @@ public: virtual void set_fpga_reset_pin(const bool reset) = 0; //! load an FPGA image - virtual boost::uint32_t load_fpga(const std::string filestring) = 0; + virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0; virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0; diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp new file mode 100644 index 000000000..e8fb8afea --- /dev/null +++ b/host/lib/usrp/b200/b200_image_loader.cpp @@ -0,0 +1,150 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/assign.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +#include <uhd/exception.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "b200_iface.hpp" +#include "b200_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +namespace uhd{ + +static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t& image_loader_args, + mboard_eeprom_t &mb_eeprom, usb_device_handle::sptr& handle, + bool user_specified){ + + std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args); + std::vector<usb_device_handle::sptr> applicable_dev_handles; + b200_iface::sptr iface; + mboard_eeprom_t eeprom; // Internal use + + if(dev_handles.size() > 0){ + BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){ + if(dev_handle->firmware_loaded()){ + iface = b200_iface::make(usb_control::make(dev_handle,0)); + eeprom = mboard_eeprom_t(*iface, "B200"); + if(user_specified){ + if(image_loader_args.args.has_key("serial") and + eeprom.get("serial") != image_loader_args.args.get("serial")){ + continue; + } + if(image_loader_args.args.has_key("name") and + eeprom.get("name") != image_loader_args.args.get("name")){ + continue; + } + applicable_dev_handles.push_back(dev_handle); + } + else applicable_dev_handles.push_back(dev_handle); + } + } + + // At this point, we should have a single B2XX + if(applicable_dev_handles.size() == 1){ + mb_eeprom = eeprom; + handle = applicable_dev_handles[0]; + return iface; + } + else if(applicable_dev_handles.size() > 1){ + std::string err_msg = "Could not resolve given args to a single B2XX device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(usb_device_handle::sptr dev_handle, applicable_dev_handles){ + eeprom = mboard_eeprom_t(*b200_iface::make(usb_control::make(dev_handle,0)), "B200"); + err_msg += str(boost::format(" * %s (serial=%s)\n") + % B2XX_STR_NAMES.get(get_b200_product(dev_handle, mb_eeprom), "B2XX") + % mb_eeprom.get("serial")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + } + + // No applicable devices found, return empty sptr so we can exit + iface.reset(); + mb_eeprom = mboard_eeprom_t(); + return iface; +} + +static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + if(!image_loader_args.load_fpga) + return false; + + bool user_specified = (image_loader_args.args.has_key("serial") or + image_loader_args.args.has_key("name")); + + // See if a B2x0 with the given args is found + mboard_eeprom_t mb_eeprom; + usb_device_handle::sptr handle; + b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, handle, user_specified); + if(!iface) return false; // No initialized B2x0 found + + std::string fpga_path; + if(image_loader_args.fpga_path == ""){ + /* + * Normally, we can auto-generate the FPGA filename from what's in the EEPROM, + * but if the applicable value is not in the EEPROM, the user must give a specific + * filename for us to use. + */ + std::string product = mb_eeprom.get("product"); + if(not B2XX_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){ + if(user_specified){ + // The user specified a bad device but expects us to know what it is + throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename."); + } + else{ + return false; + } + } + else{ + fpga_path = find_image_path(B2XX_FPGA_FILE_NAME.get(get_b200_product(handle, mb_eeprom))); + } + } + else fpga_path = image_loader_args.fpga_path; + + std::cout << boost::format("Unit: USRP %s (%s)") + % B2XX_STR_NAMES.get(get_b200_product(handle, mb_eeprom), "B2XX") + % mb_eeprom.get("serial") + << std::endl; + + iface->load_fpga(fpga_path, true); + + return true; +} + +UHD_STATIC_BLOCK(register_b200_image_loader){ + std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n" + "device, and the firmware/FPGA will be reloaded the next time\n" + "UHD uses the device."; + + image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions); +} + +} /* namespace uhd */ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0409adf30..6bd49e013 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // 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 @@ -37,20 +37,23 @@ #include <ctime> #include <cmath> +#include "../../transport/libusb1_base.hpp" + using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); +// B200 + B210: class b200_ad9361_client_t : public ad9361_params { public: ~b200_ad9361_client_t() {} double get_band_edge(frequency_band_t band) { switch (band) { - case AD9361_RX_BAND0: return 2.2e9; - case AD9361_RX_BAND1: return 4.0e9; - case AD9361_TX_BAND0: return 2.5e9; + case AD9361_RX_BAND0: return 2.2e9; // Port C + case AD9361_RX_BAND1: return 4.0e9; // Port B + case AD9361_TX_BAND0: return 2.5e9; // Port B default: return 0; } } @@ -70,25 +73,92 @@ public: } }; +// B205 +class b205_ad9361_client_t : public ad9361_params { +public: + ~b205_ad9361_client_t() {} + double get_band_edge(frequency_band_t band) { + switch (band) { + case AD9361_RX_BAND0: return 0; // Set these all to + case AD9361_RX_BAND1: return 0; // zero, so RF port A + case AD9361_TX_BAND0: return 0; // is used all the time + default: return 0; // On both Rx and Tx + } + } + clocking_mode_t get_clocking_mode() { + return AD9361_XTAL_N_CLK_PATH; + } + digital_interface_mode_t get_digital_interface_mode() { + return AD9361_DDR_FDD_LVCMOS; + } + digital_interface_delays_t get_digital_interface_timing() { + digital_interface_delays_t delays; + delays.rx_clk_delay = 0; + delays.rx_data_delay = 0x6; + delays.tx_clk_delay = 0; + delays.tx_data_delay = 0xF; + return delays; + } +}; + +/*********************************************************************** + * Helpers + **********************************************************************/ +std::string check_option_valid( + const std::string &name, + const std::vector<std::string> &valid_options, + const std::string &option +) { + if (std::find(valid_options.begin(), valid_options.end(), option) == valid_options.end()) { + throw uhd::runtime_error(str( + boost::format("Invalid option chosen for: %s") + % name + )); + } + + return option; +} + /*********************************************************************** * Discovery **********************************************************************/ //! Look up the type of B-Series device we're currently running. -// If the product ID stored in mb_eeprom is invalid, throws a -// uhd::runtime_error. -static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom) +// Throws a uhd::runtime_error if the USB PID and the product ID stored +// in the MB EEPROM are invalid, +b200_product_t get_b200_product(const usb_device_handle::sptr& handle, const mboard_eeprom_t &mb_eeprom) { + // Try USB PID first + boost::uint16_t product_id = handle->get_product_id(); + if (B2XX_PID_TO_PRODUCT.has_key(product_id)) + return B2XX_PID_TO_PRODUCT[product_id]; + + // Try EEPROM product ID code second if (mb_eeprom["product"].empty()) { throw uhd::runtime_error("B200: Missing product ID on EEPROM."); } - boost::uint16_t product_id = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); - if (not B2X0_PRODUCT_ID.has_key(product_id)) { + product_id = boost::lexical_cast<boost::uint16_t>(mb_eeprom["product"]); + if (not B2XX_PRODUCT_ID.has_key(product_id)) { throw uhd::runtime_error(str( boost::format("B200 unknown product code: 0x%04x") % product_id )); } - return B2X0_PRODUCT_ID[product_id]; + return B2XX_PRODUCT_ID[product_id]; +} + +std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint) +{ + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list; + + if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")), + uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")))); + } else { + vid_pid_pair_list = b200_vid_pid_pairs; + } + + //find the usrps and load firmware + return usb_device_handle::get_device_list(vid_pid_pair_list); } static device_addrs_t b200_find(const device_addr_t &hint) @@ -104,25 +174,13 @@ static device_addrs_t b200_find(const device_addr_t &hint) if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs; } - boost::uint16_t vid, pid; - - if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { - vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); - pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); - } else { - vid = B200_VENDOR_ID; - pid = B200_PRODUCT_ID; - } - // Important note: // The get device list calls are nested inside the for loop. // This allows the usb guts to decontruct when not in use, // so that re-enumeration after fw load can occur successfully. // This requirement is a courtesy of libusb1.0 on windows. - - //find the usrps and load firmware size_t found = 0; - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { + BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) { //extract the firmware path for the b200 std::string b200_fw_image; try{ @@ -153,7 +211,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) //search for the device until found or timeout while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0) { - BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) + BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) { usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -168,9 +226,10 @@ static device_addrs_t b200_find(const device_addr_t &hint) new_addr["serial"] = handle->get_serial(); try { // Turn the 16-Bit product ID into a string representation - new_addr["product"] = B2X0_STR_NAMES[get_b200_type(mb_eeprom)]; + new_addr["product"] = B2XX_STR_NAMES[get_b200_product(handle, mb_eeprom)]; } catch (const uhd::runtime_error &e) { // No problem if this fails -- this is just device discovery, after all. + new_addr["product"] = "B2??"; } //this is a found b200 when the hint serial and name match or blank @@ -191,7 +250,24 @@ static device_addrs_t b200_find(const device_addr_t &hint) **********************************************************************/ static device::sptr b200_make(const device_addr_t &device_addr) { - return device::sptr(new b200_impl(device_addr)); + uhd::transport::usb_device_handle::sptr handle; + + // We try twice, because the first time, the link might be in a bad state + // and we might need to reset the link, but if that didn't help, trying + // a third time is pointless. + try { + return device::sptr(new b200_impl(device_addr, handle)); + } + catch (const uhd::usb_error &e) { + libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( + boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() + )); + dev_handle->clear_endpoints(B200_USB_CTRL_RECV_ENDPOINT, B200_USB_CTRL_SEND_ENDPOINT); + dev_handle->clear_endpoints(B200_USB_DATA_RECV_ENDPOINT, B200_USB_DATA_SEND_ENDPOINT); + dev_handle->reset_device(); + } + + return device::sptr(new b200_impl(device_addr, handle)); } UHD_STATIC_BLOCK(register_b200_device) @@ -202,8 +278,10 @@ UHD_STATIC_BLOCK(register_b200_device) /*********************************************************************** * Structors **********************************************************************/ -b200_impl::b200_impl(const device_addr_t &device_addr) : +b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::sptr &handle) : + _product(B200), // Some safe value _revision(0), + _time_source(UNKNOWN), _tick_rate(0.0) // Forces a clock initialization at startup { _tree = property_tree::make(); @@ -213,16 +291,54 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //try to match the given device address with something on the USB bus boost::uint16_t vid = B200_VENDOR_ID; boost::uint16_t pid = B200_PRODUCT_ID; + bool specified_vid = false; + bool specified_pid = false; + if (device_addr.has_key("vid")) + { vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); + specified_vid = true; + } + if (device_addr.has_key("pid")) + { pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); + specified_pid = true; + } - std::vector<usb_device_handle::sptr> device_list = - usb_device_handle::get_device_list(vid, pid); + std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//search list for devices. + + // Search only for specified VID and PID if both specified + if (specified_vid && specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,pid)); + } + // Search for all supported PIDs limited to specified VID if only VID specified + else if (specified_vid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid, B210_PRODUCT_NI_ID)); + } + // Search for all supported VIDs limited to specified PID if only PID specified + else if (specified_pid) + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,pid)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,pid)); + } + // Search for all supported devices if neither VID nor PID specified + else + { + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)); + vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)); + } + + std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid_pid_pair_list); //locate the matching handle in the device list - usb_device_handle::sptr handle; BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { if (dev_handle->get_serial() == device_addr["serial"]){ handle = dev_handle; @@ -251,9 +367,9 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : std::string product_name; try { // This will throw if the product ID is invalid: - _b200_type = get_b200_type(mb_eeprom); - default_file_name = B2X0_FPGA_FILE_NAME.get(_b200_type); - product_name = B2X0_STR_NAMES.get(_b200_type); + _product = get_b200_product(handle, mb_eeprom); + default_file_name = B2XX_FPGA_FILE_NAME.get(_product); + product_name = B2XX_STR_NAMES.get(_product); } catch (const uhd::runtime_error &e) { // The only reason we may let this pass is if the user specified // the FPGA file name: @@ -262,12 +378,15 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : } // In this case, we must provide a default product name: product_name = "B200?"; - _b200_type = B200; } if (not mb_eeprom["revision"].empty()) { _revision = boost::lexical_cast<size_t>(mb_eeprom["revision"]); } + UHD_MSG(status) << "Detected Device: " << B2XX_STR_NAMES[_product] << std::endl; + + _gpsdo_capable = (_product != B205); + //////////////////////////////////////////////////////////////////// // Set up frontend mapping //////////////////////////////////////////////////////////////////// @@ -285,7 +404,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : _fe2 = 0; _gpio_state.swap_atr = 1; // Unswapped setup: - if (_b200_type == B200 and _revision >= 5) { + if (_product == B205 or (_product == B200 and _revision >= 5)) { _fe1 = 0; //map radio0 to FE1 _fe2 = 1; //map radio1 to FE2 _gpio_state.swap_atr = 0; // ATRs for radio0 are mapped to FE1 @@ -320,10 +439,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : ctrl_xport_args["send_frame_size"] = min_frame_size; ctrl_xport_args["num_send_frames"] = "16"; + // This may throw a uhd::usb_error, which will be caught by b200_make(). _ctrl_transport = usb_zero_copy::make( handle, - 4, 8, //interface, endpoint - 3, 4, //interface, endpoint + B200_USB_CTRL_RECV_INTERFACE, B200_USB_CTRL_RECV_ENDPOINT, //interface, endpoint + B200_USB_CTRL_SEND_INTERFACE, B200_USB_CTRL_SEND_ENDPOINT, //interface, endpoint ctrl_xport_args ); while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -352,34 +472,37 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// // Create the GPSDO control //////////////////////////////////////////////////////////////////// - _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); - _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); - _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup - boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation - - if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) + if (_gpsdo_capable) { - UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; - try - { - _gps = gps_ctrl::make(_async_task_data->gpsdo_uart); - } - catch(std::exception &e) - { - UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; - } - if (_gps and _gps->gps_detected()) + _async_task_data->gpsdo_uart = b200_uart::make(_ctrl_transport, B200_TX_GPS_UART_SID); + _async_task_data->gpsdo_uart->set_baud_divider(B200_BUS_CLOCK_RATE/115200); + _async_task_data->gpsdo_uart->write_uart("\n"); //cause the baud and response to be setup + boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation + + if ((_local_ctrl->peek32(RB32_CORE_STATUS) & 0xff) != B200_GPSDO_ST_NONE) { - //UHD_MSG(status) << "found" << std::endl; - BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try { - _tree->create<sensor_value_t>(mb_path / "sensors" / name) - .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + _gps = gps_ctrl::make(_async_task_data->gpsdo_uart); + } + catch(std::exception &e) + { + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_gps and _gps->gps_detected()) + { + //UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); + } + } + else + { + _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE); } - } - else - { - _local_ctrl->poke32(TOREG(SR_CORE_GPSDO_ST), B200_GPSDO_ST_NONE); } } @@ -388,7 +511,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); _tree->create<std::string>(mb_path / "name").set(product_name); - _tree->create<std::string>(mb_path / "codename").set("Sasquatch"); + _tree->create<std::string>(mb_path / "codename").set((_product == B205) ? "Pixie" : "Sasquatch"); //////////////////////////////////////////////////////////////////// // Create data transport @@ -402,10 +525,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192"); data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); + // This may throw a uhd::usb_error, which will be caught by b200_make(). _data_transport = usb_zero_copy::make( handle, // identifier - 2, 6, // IN interface, endpoint - 1, 2, // OUT interface, endpoint + B200_USB_DATA_RECV_INTERFACE, B200_USB_DATA_RECV_ENDPOINT, //interface, endpoint + B200_USB_DATA_SEND_INTERFACE, B200_USB_DATA_SEND_ENDPOINT, //interface, endpoint data_xport_args // param hints ); while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -415,13 +539,20 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : // create time and clock control objects //////////////////////////////////////////////////////////////////// _spi_iface = b200_local_spi_core::make(_local_ctrl); - _adf4001_iface = boost::make_shared<b200_ref_pll_ctrl>(_spi_iface); + if (_product != B205) { + _adf4001_iface = boost::make_shared<b200_ref_pll_ctrl>(_spi_iface); + } //////////////////////////////////////////////////////////////////// // Init codec - turns on clocks //////////////////////////////////////////////////////////////////// UHD_MSG(status) << "Initialize CODEC control..." << std::endl; - ad9361_params::sptr client_settings = boost::make_shared<b200_ad9361_client_t>(); + ad9361_params::sptr client_settings; + if (_product == B205) { + client_settings = boost::make_shared<b205_ad9361_client_t>(); + } else { + client_settings = boost::make_shared<b200_ad9361_client_t>(); + } _codec_ctrl = ad9361_ctrl::make_spi(client_settings, _spi_iface, AD9361_SLAVENO); this->reset_codec_dcm(); @@ -447,6 +578,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : .publish(boost::bind(&b200_impl::get_tick_rate, this)) .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1)); _tree->create<time_spec_t>(mb_path / "time" / "cmd"); + _tree->create<bool>(mb_path / "auto_tick_rate").set(false); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors @@ -477,15 +609,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : UHD_ASSERT_THROW(num_radio_chains > 0); UHD_ASSERT_THROW(num_radio_chains <= 2); _radio_perifs.resize(num_radio_chains); - for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i); + _codec_mgr = ad936x_manager::make(_codec_ctrl, num_radio_chains); + _codec_mgr->init_codec(); + for (size_t i = 0; i < _radio_perifs.size(); i++) + this->setup_radio(i); + //now test each radio module's connection to the codec interface - _codec_ctrl->data_port_loopback(true); BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - this->codec_loopback_self_test(perif.ctrl); + _codec_mgr->loopback_self_test(perif.ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK); } - _codec_ctrl->data_port_loopback(false); //register time now and pps onto available radio cores _tree->create<time_spec_t>(mb_path / "time" / "now") @@ -501,15 +635,36 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : } //setup time source props + static const std::vector<std::string> time_sources = (_gpsdo_capable) ? + boost::assign::list_of("none")("internal")("external")("gpsdo") : + boost::assign::list_of("none")("internal")("external") ; + _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options") + .set(time_sources); _tree->create<std::string>(mb_path / "time_source" / "value") + .coerce(boost::bind(&check_option_valid, "time source", time_sources, _1)) .subscribe(boost::bind(&b200_impl::update_time_source, this, _1)); - static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); - _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); //setup reference source props + static const std::vector<std::string> clock_sources = (_gpsdo_capable) ? + boost::assign::list_of("internal")("external")("gpsdo") : + boost::assign::list_of("internal")("external") ; + _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options") + .set(clock_sources); _tree->create<std::string>(mb_path / "clock_source" / "value") + .coerce(boost::bind(&check_option_valid, "clock source", clock_sources, _1)) .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo"); - _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources); + + //////////////////////////////////////////////////////////////////// + // front panel gpio + //////////////////////////////////////////////////////////////////// + _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) + { + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) + .set(0) + .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); + } + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") + .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); //////////////////////////////////////////////////////////////////// // dboard eeproms but not really @@ -524,7 +679,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //////////////////////////////////////////////////////////////////// //init the clock rate to something reasonable - double default_tick_rate = device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE); + double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE); _tree->access<double>(mb_path / "tick_rate").set(default_tick_rate); //subdev spec contains full width of selections @@ -542,27 +697,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) : //init to internal clock and time source _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); - _tree->access<std::string>(mb_path / "time_source/value").set("none"); + _tree->access<std::string>(mb_path / "time_source/value").set("internal"); // Set the DSP chains to some safe value for (size_t i = 0; i < _radio_perifs.size(); i++) { - _radio_perifs[i].ddc->set_host_rate(default_tick_rate / B200_DEFAULT_DECIM); - _radio_perifs[i].duc->set_host_rate(default_tick_rate / B200_DEFAULT_INTERP); + _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM); + _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP); } - - //GPS installed: use external ref, time, and init time spec - if (_gps and _gps->gps_detected()) - { - UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); - _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - } else { - //init to internal clock and time source - _tree->access<std::string>(mb_path / "clock_source/value").set("internal"); - _tree->access<std::string>(mb_path / "time_source/value").set("internal"); + // We can automatically choose a master clock rate, but not if the user specifies one + _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); + if (not device_addr.has_key("master_clock_rate")) { + UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; } } @@ -585,10 +730,18 @@ void b200_impl::setup_radio(const size_t dspno) const fs_path mb_path = "/mboards/0"; //////////////////////////////////////////////////////////////////// + // Set up transport + //////////////////////////////////////////////////////////////////// + const boost::uint32_t sid = (dspno == 0) ? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID; + + //////////////////////////////////////////////////////////////////// // radio control //////////////////////////////////////////////////////////////////// - const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID; - perif.ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid); + perif.ctrl = radio_ctrl_core_3000::make( + false/*lilE*/, + _ctrl_transport, + zero_copy_if::sptr()/*null*/, + sid); perif.ctrl->hold_task(_async_task); _async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak _tree->access<time_spec_t>(mb_path / "time" / "cmd") @@ -596,121 +749,96 @@ void b200_impl::setup_radio(const size_t dspno) _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); this->register_loopback_self_test(perif.ctrl); - perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // Set up peripherals //////////////////////////////////////////////////////////////////// + perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); + // create rx dsp control objects perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/); perif.ddc->set_link_rate(10e9/8); //whatever perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false); + perif.ddc->set_freq(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ); + perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + perif.duc->set_freq(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = RB64_TIME_NOW; + time64_rb_bases.rb_pps = RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + + //////////////////////////////////////////////////////////////////// + // connect rx dsp control objects + //////////////////////////////////////////////////////////////////// _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / dspno; - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .set(0.0) // We can only load a sensible value after the tick rate was set - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") + .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1)) ; - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(0.0); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// // create tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / dspno; - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .set(0.0) // We can only load a sensible value after the tick rate was set - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") + .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1)) .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1)) ; - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(0.0); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); //////////////////////////////////////////////////////////////////// // create RF frontend interfacing //////////////////////////////////////////////////////////////////// - for(size_t direction = 0; direction < 2; direction++) - { - const std::string x = direction? "rx" : "tx"; - const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == _fe1)? "1" : "2")); - const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A"); - - _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); - _tree->create<int>(rf_fe_path / "sensors"); + static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); + BOOST_FOREACH(direction_t dir, dirs) { + const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx"; + const std::string key = std::string(((dir == RX_DIRECTION) ? "RX" : "TX")) + std::string(((dspno == _fe1) ? "1" : "2")); + const fs_path rf_fe_path + = mb_path / "dboards" / "A" / (x + "_frontends") / (dspno ? "B" : "A"); + + // This will connect all the AD936x-specific items + _codec_mgr->populate_frontend_subtree( + _tree->subtree(rf_fe_path), key, dir + ); + + // Now connect all the b200_impl-specific items _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") - .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, x == "tx")); - BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) - { - _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") - .set(ad9361_ctrl::get_gain_range(key)); - - _tree->create<double>(rf_fe_path / "gains" / name / "value") - .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) - .set(x == "rx" ? B200_DEFAULT_RX_GAIN : B200_DEFAULT_TX_GAIN); - } - _tree->create<std::string>(rf_fe_path / "connection").set("IQ"); - _tree->create<bool>(rf_fe_path / "enabled").set(true); - _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); - _tree->create<double>(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(40e6); - _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create<double>(rf_fe_path / "freq" / "value") - .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) - .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION)) + ; + _tree->access<double>(rf_fe_path / "freq" / "value") .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1)) - .set(B200_DEFAULT_FREQ); - _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") - .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); - - //setup RX related stuff - if (key[0] == 'R') + ; + if (dir == RX_DIRECTION) { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1)) - .set("RX2"); - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi") - .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); + .set("RX2") + ; + } - if (key[0] == 'T') + else if (dir == TX_DIRECTION) { static const std::vector<std::string> ants(1, "TX/RX"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX"); } - } } @@ -733,34 +861,10 @@ void b200_impl::register_loopback_self_test(wb_iface::sptr iface) UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl; } -void b200_impl::codec_loopback_self_test(wb_iface::sptr iface) -{ - UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; - size_t hash = size_t(time(NULL)); - for (size_t i = 0; i < 100; i++) - { - boost::hash_combine(hash, i); - const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; - iface->poke32(TOREG(SR_CODEC_IDLE), word32); - iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate - const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); - const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); - const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); - bool test_fail = word32 != rb_tx or word32 != rb_rx; - if (test_fail) { - UHD_MSG(status) << "fail" << std::endl; - throw uhd::runtime_error("CODEC loopback test failed."); - } - } - UHD_MSG(status) << "pass" << std::endl; - /* Zero out the idle data. */ - iface->poke32(TOREG(SR_CODEC_IDLE), 0); -} - /*********************************************************************** * Sample and tick rate comprehension below **********************************************************************/ -void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction /*= ""*/) { const size_t max_chans = 2; if (chan_count > max_chans) @@ -768,7 +872,7 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co throw uhd::value_error(boost::str( boost::format("cannot not setup %d %s channels (maximum is %d)") % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) % max_chans )); } @@ -782,20 +886,26 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co % (tick_rate/1e6) % (max_tick_rate/1e6) % chan_count - % (direction ? direction : "data") + % (direction.empty() ? "data" : direction) )); } } } -double b200_impl::set_tick_rate(const double rate) +double b200_impl::set_tick_rate(const double new_tick_rate) { - UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz\n") % (rate/1e6)); - - check_tick_rate_with_current_streamers(rate); // Defined in b200_io_impl.cpp + UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz... ") % (new_tick_rate/1e6)) << std::flush; + check_tick_rate_with_current_streamers(new_tick_rate); // Defined in b200_io_impl.cpp + + // Make sure the clock rate is actually changed before doing + // the full Monty of setting regs and loopback tests etc. + if (std::abs(new_tick_rate - _tick_rate) < 1.0) { + UHD_MSG(status) << "OK" << std::endl; + return _tick_rate; + } - _tick_rate = _codec_ctrl->set_clock_rate(rate); - UHD_MSG(status) << (boost::format("Actually got clock rate %.6f MHz\n") % (_tick_rate/1e6)); + _tick_rate = _codec_ctrl->set_clock_rate(new_tick_rate); + UHD_MSG(status) << std::endl << (boost::format("Actually got clock rate %.6f MHz.") % (_tick_rate/1e6)) << std::endl; //reset after clock rate change this->reset_codec_dcm(); @@ -839,12 +949,14 @@ void b200_impl::check_fpga_compat(void) if (signature != 0xACE0BA5E) throw uhd::runtime_error( "b200::check_fpga_compat signature register readback failed"); - if (compat_major != B200_FPGA_COMPAT_NUM){ + const boost::uint16_t expected = (_product == B205 ? B205_FPGA_COMPAT_NUM : B200_FPGA_COMPAT_NUM); + if (compat_major != expected) + { throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA build is not compatible with the host code build.\n" "%s" - ) % int(B200_FPGA_COMPAT_NUM) % compat_major % print_utility_error("uhd_images_downloader.py"))); + ) % int(expected) % compat_major % print_utility_error("uhd_images_downloader.py"))); } _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % compat_major % compat_minor)); @@ -856,41 +968,118 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom) } +boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) +{ + return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) +{ + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } +} + /*********************************************************************** * Reference time and clock **********************************************************************/ void b200_impl::update_clock_source(const std::string &source) { - // present the PLL with a valid 10 MHz signal before switching its reference - _gpio_state.ref_sel = (source == "gpsdo")? 1 : 0; - this->update_gpio_state(); + // For B205, ref_sel selects whether or not to lock to the external clock source + if (_product == B205) + { + if (source == "external" and _time_source == EXTERNAL) + { + throw uhd::value_error("external reference cannot be both a clock source and a time source"); + } - if (source == "internal"){ - _adf4001_iface->set_lock_to_ext_ref(false); + if (source == "internal") + { + if (_gpio_state.ref_sel != 0) + { + _gpio_state.ref_sel = 0; + this->update_gpio_state(); + } + } + else if (source == "external") + { + if (_gpio_state.ref_sel != 1) + { + _gpio_state.ref_sel = 1; + this->update_gpio_state(); + } + } + else + { + throw uhd::key_error("update_clock_source: unknown source: " + source); + } + return; } - else if ((source == "external") - or (source == "gpsdo")){ + // For all other devices, ref_sel selects the external or gpsdo clock source + // and the ADF4001 selects whether to lock to it or not + if (source == "internal") + { + _adf4001_iface->set_lock_to_ext_ref(false); + } + else if (source == "external") + { + if (_gpio_state.ref_sel != 0) + { + _gpio_state.ref_sel = 0; + this->update_gpio_state(); + } _adf4001_iface->set_lock_to_ext_ref(true); - } else { + } + else if (_gps and source == "gpsdo") + { + if (_gpio_state.ref_sel != 1) + { + _gpio_state.ref_sel = 1; + this->update_gpio_state(); + } + _adf4001_iface->set_lock_to_ext_ref(true); + } + else + { throw uhd::key_error("update_clock_source: unknown source: " + source); } } void b200_impl::update_time_source(const std::string &source) { - boost::uint32_t value = 0; + if (_product == B205 and source == "external" and _gpio_state.ref_sel == 1) + { + throw uhd::value_error("external reference cannot be both a time source and a clock source"); + } + + // We assume source is valid for this device (if it's gone through + // the prop three, then it definitely is thanks to our coercer) + time_source_t value; if (source == "none") - value = 3; + value = NONE; else if (source == "internal") - value = 2; + value = INTERNAL; else if (source == "external") - value = 1; - else if (source == "gpsdo") - value = 0; - else throw uhd::key_error("update_time_source: unknown source: " + source); - _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), value); + value = EXTERNAL; + else if (_gps and source == "gpsdo") + value = GPSDO; + else + throw uhd::key_error("update_time_source: unknown source: " + source); + if (_time_source != value) + { + _local_ctrl->poke32(TOREG(SR_CORE_PPS_SEL), value); + _time_source = value; + } } /*********************************************************************** @@ -899,6 +1088,11 @@ void b200_impl::update_time_source(const std::string &source) void b200_impl::update_bandsel(const std::string& which, double freq) { + // B205 does not have bandsels + if (_product == B205) { + return; + } + if(which[0] == 'R') { if(freq < 2.2e9) { _gpio_state.rx_bandsel_a = 0; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 65796d1a4..b291f8e5c 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // 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 @@ -22,6 +22,7 @@ #include "b200_uart.hpp" #include "b200_cores.hpp" #include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" #include "adf4001_ctrl.hpp" #include "rx_vita_core_3000.hpp" #include "tx_vita_core_3000.hpp" @@ -43,18 +44,14 @@ #include <uhd/usrp/gps_ctrl.hpp> #include <uhd/transport/usb_zero_copy.hpp> #include <uhd/transport/bounded_buffer.hpp> +#include <boost/assign.hpp> #include <boost/weak_ptr.hpp> #include "recv_packet_demuxer_3000.hpp" -static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 7; +static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 8; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 10; +static const boost::uint16_t B205_FPGA_COMPAT_NUM = 1; static const double B200_BUS_CLOCK_RATE = 100e6; -static const double B200_DEFAULT_TICK_RATE = 32e6; -static const double B200_DEFAULT_FREQ = 100e6; // Hz -static const double B200_DEFAULT_DECIM = 128; -static const double B200_DEFAULT_INTERP = 128; -static const double B200_DEFAULT_RX_GAIN = 0; // dB -static const double B200_DEFAULT_TX_GAIN = 0; // dB static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83; static const size_t B200_MAX_RATE_USB2 = 53248000; // bytes/s static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s @@ -82,32 +79,60 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040; static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID); -/*********************************************************************** - * The B200 Capability Constants - **********************************************************************/ +static const unsigned char B200_USB_CTRL_RECV_INTERFACE = 4; +static const unsigned char B200_USB_CTRL_RECV_ENDPOINT = 8; +static const unsigned char B200_USB_CTRL_SEND_INTERFACE = 3; +static const unsigned char B200_USB_CTRL_SEND_ENDPOINT = 4; + +static const unsigned char B200_USB_DATA_RECV_INTERFACE = 2; +static const unsigned char B200_USB_DATA_RECV_ENDPOINT = 6; +static const unsigned char B200_USB_DATA_SEND_INTERFACE = 1; +static const unsigned char B200_USB_DATA_SEND_ENDPOINT = 2; + +/* + * VID/PID pairs for all B2xx products + */ +static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs = + boost::assign::list_of + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B205_PRODUCT_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)) + (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)) + ; + +b200_product_t get_b200_product(const uhd::transport::usb_device_handle::sptr& handle, const uhd::usrp::mboard_eeprom_t &mb_eeprom); +std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint); //! Implementation guts class b200_impl : public uhd::device { public: //structors - b200_impl(const uhd::device_addr_t &); + b200_impl(const uhd::device_addr_t &, uhd::transport::usb_device_handle::sptr &handle); ~b200_impl(void); //the io interface uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); bool recv_async_msg(uhd::async_metadata_t &, double); - void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); + + //! Check that the combination of stream args and tick rate are valid. + // + // Basically figures out the arguments for enforce_tick_rate_limits() + // and calls said method. If arguments are invalid, throws a + // uhd::value_error. + void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = ""); private: - b200_type_t _b200_type; - size_t _revision; + b200_product_t _product; + size_t _revision; + bool _gpsdo_capable; //controllers b200_iface::sptr _iface; radio_ctrl_core_3000::sptr _local_ctrl; uhd::usrp::ad9361_ctrl::sptr _codec_ctrl; + uhd::usrp::ad936x_manager::sptr _codec_mgr; b200_local_spi_core::sptr _spi_iface; boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface; uhd::gps_ctrl::sptr _gps; @@ -136,7 +161,6 @@ private: boost::optional<uhd::msg_task::msg_type_t> handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>); void register_loopback_self_test(uhd::wb_iface::sptr iface); - void codec_loopback_self_test(uhd::wb_iface::sptr iface); void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void check_fw_compat(void); void check_fpga_compat(void); @@ -154,6 +178,7 @@ private: { radio_ctrl_core_3000::sptr ctrl; gpio_core_200_32wo::sptr atr; + gpio_core_200::sptr fp_gpio; time_core_3000::sptr time64; rx_vita_core_3000::sptr framer; rx_dsp_core_3000::sptr ddc; @@ -194,20 +219,71 @@ private: } } _gpio_state; + enum time_source_t {GPSDO=0,EXTERNAL=1,INTERNAL=2,NONE=3,UNKNOWN=4} _time_source; + void update_gpio_state(void); void reset_codec_dcm(void); void update_enables(void); void update_atrs(void); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + double _tick_rate; double get_tick_rate(void){return _tick_rate;} double set_tick_rate(const double rate); + + /*! \brief Choose a tick rate (master clock rate) that works well for the given sampling rate. + * + * This function will try and choose a master clock rate automatically. + * See the function definition for details on the algorithm. + * + * The chosen tick rate is the largest multiple of two that is smaler + * than the max tick rate. + * The base rate is either given explicitly, or is the lcm() of the tx + * and rx sampling rates. In that case, it reads the rates directly + * from the property tree. It also tries to guess the number of channels + * (for the max possible tick rate) by checking the available streamers. + * This value, too, can explicitly be given. + * + * \param rate If this is given, it will be used as a minimum rate, or + * argument to lcm(). + * \param tree_dsp_path The sampling rate from this property tree path + * will be ignored. + * \param num_chans If given, specifies the number of channels. + */ + void set_auto_tick_rate( + const double rate=0, + const uhd::fs_path &tree_dsp_path="", + size_t num_chans=0 + ); + void update_tick_rate(const double); - void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL); + + /*! Check if \p tick_rate works with \p chan_count channels. + * + * Throws a uhd::value_error if not. + */ + void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction = ""); void check_tick_rate_with_current_streamers(double rate); + /*! Return the max number of channels on active rx_streamer or tx_streamer objects associated with this device. + * + * \param direction Set to "TX" to only check tx_streamers, "RX" to only check + * rx_streamers. Any other value will check if \e any active + * streamers are available. + * \return Return the number of tx streamers (direction=="TX"), the number of rx + * streamers (direction=="RX") or the total number of streamers. + */ + size_t max_chan_count(const std::string &direction=""); + + //! Coercer, attached to the "rate/value" property on the rx dsps. + double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double); void update_rx_samp_rate(const size_t, const double); + + //! Coercer, attached to the "rate/value" property on the tx dsps. + double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double); void update_tx_samp_rate(const size_t, const double); }; diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 1e11e7ff6..4aa1a46af 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2014 Ettus Research LLC // // 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 @@ -21,8 +21,10 @@ #include "../../transport/super_recv_packet_handler.hpp" #include "../../transport/super_send_packet_handler.hpp" #include "async_packet_handler.hpp" +#include <uhd/utils/math.hpp> #include <boost/bind.hpp> #include <boost/make_shared.hpp> +#include <boost/math/common_factor.hpp> #include <set> using namespace uhd; @@ -34,30 +36,32 @@ using namespace uhd::transport; **********************************************************************/ void b200_impl::check_tick_rate_with_current_streamers(double rate) { - size_t max_tx_chan_count = 0, max_rx_chan_count = 0; + // Defined in b200_impl.cpp + enforce_tick_rate_limits(max_chan_count("RX"), rate, "RX"); + enforce_tick_rate_limits(max_chan_count("TX"), rate, "TX"); +} + +// direction can either be "TX", "RX", or empty (default) +size_t b200_impl::max_chan_count(const std::string &direction /* = "" */) +{ + size_t max_count = 0; BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { - { + if ((direction == "RX" or direction.empty()) and not perif.rx_streamer.expired()) { boost::shared_ptr<sph::recv_packet_streamer> rx_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (rx_streamer) - max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels()); + max_count = std::max(max_count, rx_streamer->get_num_channels()); } - - { + if ((direction == "TX" or direction.empty()) and not perif.tx_streamer.expired()) { boost::shared_ptr<sph::send_packet_streamer> tx_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (tx_streamer) - max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels()); + max_count = std::max(max_count, tx_streamer->get_num_channels()); } } - - // Defined in b200_impl.cpp - enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); - enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); + return max_count; } -void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction /*= ""*/) { std::set<size_t> chans_set; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) @@ -69,33 +73,115 @@ void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_ enforce_tick_rate_limits(chans_set.size(), tick_rate, direction); // Defined in b200_impl.cpp } -void b200_impl::update_tick_rate(const double rate) +void b200_impl::set_auto_tick_rate( + const double rate, + const fs_path &tree_dsp_path, + size_t num_chans +) { + if (num_chans == 0) { // Divine them + num_chans = std::max(size_t(1), max_chan_count()); + } + const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans; + if (rate != 0.0 and + (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ))) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (rate / 1e6) % (max_tick_rate / 1e6) + )); + } + + // See also the doxygen documentation for these steps in b200_impl.hpp + // Step 1: Obtain LCM and max rate from all relevant dsps + boost::uint32_t lcm_rate = (rate == 0) ? 1 : static_cast<boost::uint32_t>(floor(rate + 0.5)); + for (int i = 0; i < 2; i++) { // Loop through rx and tx + std::string dir = (i == 0) ? "tx" : "rx"; + // We have no way of knowing which DSPs are used, so we check them all. + BOOST_FOREACH(const std::string &dsp_no, _tree->list(str(boost::format("/mboards/0/%s_dsps") % dir))) { + fs_path dsp_path = str(boost::format("/mboards/0/%s_dsps/%s") % dir % dsp_no); + if (dsp_path == tree_dsp_path) { + continue; + } + double this_dsp_rate = _tree->access<double>(dsp_path / "rate/value").get(); + // Check if the user selected something completely unreasonable: + if (uhd::math::fp_compare::fp_compare_delta<double>(this_dsp_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + throw uhd::value_error(str( + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") + % (this_dsp_rate / 1e6) % (max_tick_rate / 1e6) + )); + } + // If this_dsp_rate == 0.0, the sampling rate for this DSP hasn't been set, so + // we don't take that into consideration. + if (this_dsp_rate == 0.0) { + continue; + } + lcm_rate = boost::math::lcm<boost::uint32_t>( + lcm_rate, + static_cast<boost::uint32_t>(floor(this_dsp_rate + 0.5)) + ); + } + } + if (lcm_rate == 1) { + // In this case, no one has ever set a sampling rate. + return; + } + + double base_rate = static_cast<double>(lcm_rate); + try { + // Step 2: Get a good tick rate value + const double new_rate = _codec_mgr->get_auto_tick_rate(base_rate, num_chans); + // Step 3: Set the new tick rate value (if any change) + if (!uhd::math::frequencies_are_equal(_tree->access<double>("/mboards/0/tick_rate").get(), new_rate)) { + _tree->access<double>("/mboards/0/tick_rate").set(new_rate); + } + } catch (const uhd::value_error &e) { + UHD_MSG(warning) + << "Cannot automatically determine an appropriate tick rate for these sampling rates." << std::endl + << "Consider using different sampling rates, or manually specify a suitable master clock rate." << std::endl; + return; // Let the others handle this + } +} + +void b200_impl::update_tick_rate(const double new_tick_rate) { - check_tick_rate_with_current_streamers(rate); + check_tick_rate_with_current_streamers(new_tick_rate); BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.framer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.framer->set_tick_rate(new_tick_rate); } BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) { boost::shared_ptr<sph::send_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); - if (my_streamer) my_streamer->set_tick_rate(rate); - perif.deframer->set_tick_rate(_tick_rate); + if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); + perif.deframer->set_tick_rate(new_tick_rate); } } -#define CHECK_BANDWIDTH(dir) \ - if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ - UHD_MSG(warning) \ - << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ - << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ - << std::endl; \ +#define CHECK_RATE_AND_THROW(rate) \ + if (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > \ + uhd::math::fp_compare::fp_compare_delta<double>(ad9361_device_t::AD9361_MAX_CLOCK_RATE, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { \ + throw uhd::value_error(str( \ + boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate.") \ + % (rate / 1e6) \ + )); \ + } + +double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate) +{ + // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(rx_rate); + const std::string dsp_path = (boost::format("/mboards/0/rx_dsps/%s") % dspno).str(); + set_auto_tick_rate(rx_rate, dsp_path); } + return ddc->set_host_rate(rx_rate); +} void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) { @@ -105,7 +191,18 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate) my_streamer->set_samp_rate(rate); const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment(); my_streamer->set_scale_factor(adj); - CHECK_BANDWIDTH("Rx"); + _codec_mgr->check_bandwidth(rate, "Rx"); +} + +double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate) +{ + // Have to set tick rate first, or the duc will change the requested rate based on default tick rate + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + CHECK_RATE_AND_THROW(tx_rate); + const std::string dsp_path = (boost::format("/mboards/0/tx_dsps/%s") % dspno).str(); + set_auto_tick_rate(tx_rate, dsp_path); + } + return duc->set_host_rate(tx_rate); } void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) @@ -116,7 +213,7 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) my_streamer->set_samp_rate(rate); const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment(); my_streamer->set_scale_factor(adj); - CHECK_BANDWIDTH("Tx"); + _codec_mgr->check_bandwidth(rate, "Tx"); } /*********************************************************************** @@ -131,7 +228,7 @@ uhd::usrp::subdev_spec_t b200_impl::coerce_subdev_spec(const uhd::usrp::subdev_s // // Any other spec is probably illegal and will be caught by // validate_subdev_spec(). - if (spec.size() and _b200_type == B200 and spec[0].sd_name == "B") { + if (spec.size() and (_product == B200 or _product == B205) and spec[0].sd_name == "B") { spec[0].sd_name = "A"; } return spec; @@ -276,6 +373,9 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::recv_packet_streamer> my_streamer; @@ -383,7 +483,10 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_) if (args.otw_format.empty()) args.otw_format = "sc16"; args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; - check_streamer_args(args, this->get_tick_rate(), "TX"); + if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { + set_auto_tick_rate(0, "", args.channels.size()); + } + check_streamer_args(args, this->get_tick_rate(), "RX"); boost::shared_ptr<sph::send_packet_streamer> my_streamer; for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp index 900651f94..8f2dd03f3 100644 --- a/host/lib/usrp/b200/b200_regs.hpp +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -46,11 +46,13 @@ localparam SR_TX_DSP = 184; localparam SR_TIME = 128; localparam SR_RX_FMT = 136; localparam SR_TX_FMT = 138; +localparam SR_FP_GPIO = 200; localparam RB32_TEST = 0; localparam RB64_TIME_NOW = 8; localparam RB64_TIME_PPS = 16; localparam RB64_CODEC_READBACK = 24; +localparam RB32_FP_GPIO = 32; //pll constants static const int AD9361_SLAVENO = (1 << 0); diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 129cc569b..e63a09935 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -33,6 +33,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 65e8e2df9..54f0fcdbf 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -16,7 +16,6 @@ // #include "ad9361_ctrl.hpp" -#include <uhd/exception.hpp> #include <uhd/types/ranges.hpp> #include <uhd/utils/msg.hpp> #include <uhd/types/serial.hpp> @@ -108,6 +107,27 @@ public: return _device.set_gain(direction, chain, value); } + void set_agc(const std::string &which, bool enable) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + _device.set_agc(chain, enable); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + boost::lock_guard<boost::mutex> lock(_mutex); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + if(mode == "slow") { + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC); + } else if (mode == "fast"){ + _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC); + } else { + throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option."); + } + } + //! set a new clock rate, return the exact value double set_clock_rate(const double rate) { @@ -175,6 +195,67 @@ public: return sensor_value_t("RSSI", _device.get_rssi(chain), "dB"); } + //! read the internal temp sensor. Average over 3 results + sensor_value_t get_temperature() + { + return sensor_value_t("temp", _device.get_average_temperature(), "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_dc_offset_auto(direction,on); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + _device.set_iq_balance_auto(direction,on); + } + + double set_bw_filter(const std::string &which, const double bw) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.set_bw_filter(direction, bw); + } + + std::vector<std::string> get_filter_names(const std::string &which) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + return _device.get_filter_names(direction); + } + + filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); + return _device.get_filter(direction, chain, filter_name); + } + + void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter) + { + boost::lock_guard<boost::mutex> lock(_mutex); + + ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); + ad9361_device_t::chain_t chain = _get_chain_from_antenna(which); + _device.set_filter(direction, chain, filter_name, filter); + } + + void output_digital_test_tone(bool enb) + { + _device.digital_test_tone(enb); + } + private: static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna) { diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index b7d7b8e26..5c438ee9c 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -22,15 +22,31 @@ #include <uhd/types/ranges.hpp> #include <uhd/types/serial.hpp> #include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp> #include <boost/shared_ptr.hpp> #include <ad9361_device.h> #include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector> namespace uhd { namespace usrp { -/*********************************************************************** - * AD9361 Control Interface - **********************************************************************/ +/*! AD936x Control Interface + * + * This is a convenient way to access the AD936x RF IC. + * It basically encodes knowledge of register values etc. into + * accessible API calls. + * + * \section ad936x_which The `which` parameter + * + * Many function calls require a `which` parameter to select + * the RF frontend. Valid values for `which` are: + * - RX1, RX2 + * - TX1, TX2 + * + * Frontend numbering is as designed by the AD9361. + */ class ad9361_ctrl : public boost::noncopyable { public: @@ -77,15 +93,18 @@ public: return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end } - //! set the filter bandwidth for the frontend - double set_bw_filter(const std::string &/*which*/, const double /*bw*/) - { - return 56e6; //TODO - } + //! set the filter bandwidth for the frontend's analog low pass + virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0; //! set the gain for a particular gain element virtual double set_gain(const std::string &which, const double value) = 0; + //! Enable or disable the AGC module + virtual void set_agc(const std::string &which, bool enable) = 0; + + //! configure the AGC module to slow or fast mode + virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; + //! set a new clock rate, return the exact value virtual double set_clock_rate(const double rate) = 0; @@ -95,14 +114,48 @@ public: //! tune the given frontend, return the exact value virtual double tune(const std::string &which, const double value) = 0; + //! set the DC offset for I and Q manually + void set_dc_offset(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); + } + + //! enable or disable the BB/RF DC tracking feature + virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + + //! set the IQ correction value manually + void set_iq_balance(const std::string &, const std::complex<double>) + { + //This feature should not be used according to Analog Devices + throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); + } + + //! enable or disable the quadrature calibration + virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; + //! get the current frequency for the given frontend virtual double get_freq(const std::string &which) = 0; - //! turn on/off data port loopback + //! turn on/off Catalina's data port loopback virtual void data_port_loopback(const bool on) = 0; //! read internal RSSI sensor virtual sensor_value_t get_rssi(const std::string &which) = 0; + + //! read the internal temp sensor + virtual sensor_value_t get_temperature() = 0; + + //! List all available filters by name + virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + + //! Return a list of all filters + virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + + //! Write back a filter + virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0; + + virtual void output_digital_test_tone(bool enb) = 0; }; }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_client.h b/host/lib/usrp/common/ad9361_driver/ad9361_client.h index 5e848d4c0..e9ea1404a 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_client.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_client.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_CLIENT_H diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index db5de52d0..0a8a61575 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #include "ad9361_filter_taps.h" @@ -11,6 +24,7 @@ #include <cmath> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> #include <boost/cstdint.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread/thread.hpp> @@ -78,16 +92,21 @@ int get_num_taps(int max_num_taps) { const double ad9361_device_t::AD9361_MAX_GAIN = 89.75; const double ad9361_device_t::AD9361_MAX_CLOCK_RATE = 61.44e6; +const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6; // Max bandwdith is due to filter rolloff in analog filter stage const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; +/* Startup RF frequencies */ +const double ad9361_device_t::DEFAULT_RX_FREQ = 800e6; +const double ad9361_device_t::DEFAULT_TX_FREQ = 850e6; + /* Program either the RX or TX FIR filter. * * The process is the same for both filters, but the function must be told * how many taps are in the filter, and given a vector of the taps * themselves. */ -void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs) +void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs) { boost::uint16_t base; @@ -102,8 +121,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Encode number of filter taps for programming register */ boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + boost::uint8_t reg_chain = 0; + switch (chain) { + case CHAIN_1: + reg_chain = 0x01 << 3; + break; + case CHAIN_2: + reg_chain = 0x02 << 3; + break; + default: + reg_chain = 0x03 << 3; + } + /* Turn on the filter clock. */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1a); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02); boost::this_thread::sleep(boost::posix_time::milliseconds(1)); /* Zero the unused taps just in case they have stale data */ @@ -112,7 +143,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, 0x0); _io_iface->poke8(base + 2, 0x0); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -122,7 +153,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b _io_iface->poke8(base + 0, addr); _io_iface->poke8(base + 1, (coeffs[addr]) & 0xff); _io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff); - _io_iface->poke8(base + 5, reg_numtaps | 0x1e); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2)); _io_iface->poke8(base + 4, 0x00); _io_iface->poke8(base + 4, 0x00); } @@ -133,9 +164,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table" */ - _io_iface->poke8(base + 5, reg_numtaps | 0x1A); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1)); if (direction == RX) { - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); /* Rx Gain, set to prevent digital overflow/saturation in filters 0:+6dB, 1:0dB, 2:-6dB, 3:-12dB page 35 of UG-671 */ @@ -144,7 +175,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b /* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters 0: 0dB, 1:-6dB page 25 of UG-671 */ - _io_iface->poke8(base + 5, reg_numtaps | 0x18); + _io_iface->poke8(base + 5, reg_numtaps | reg_chain ); } } @@ -175,7 +206,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation) } } - _program_fir_filter(RX, num_taps, coeffs.get()); + _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get()); } /* Program the TX FIR Filter. */ @@ -207,7 +238,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio } } - _program_fir_filter(TX, num_taps, coeffs.get()); + _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get()); } /*********************************************************************** @@ -282,16 +313,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the RX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_rx_analog_filter() + * rate. + * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.143e6. + * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28 + * Min filter BW is 200kHz. 200 / 1.4 = 143 */ if (bbbw > 28e6) { bbbw = 28e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + } else if (bbbw < 0.143e6) { + bbbw = 0.143e6; } double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2); @@ -340,16 +379,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter() * * Note that the filter calibration depends heavily on the baseband * bandwidth, so this must be re-done after any change to the TX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_tx_analog_filter() + * rate. + * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.391e6. + * Max filter BW is 32 MHz. 32 / 1.6 = 20 + * Min filter BW is 625 kHz. 625 / 1.6 = 391 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.625e6) { - bbbw = 0.625e6; + } else if (bbbw < 0.391e6) { + bbbw = 0.391e6; } double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2); @@ -386,16 +434,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter() /* Calibrate the secondary TX filter. * * This filter also depends on the TX sample rate, so if a rate change is - * made, the previous calibration will no longer be valid. */ -void ad9361_device_t::_calibrate_secondary_tx_filter() + * made, the previous calibration will no longer be valid. + * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/ +double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw) { - /* For filter tuning, baseband BW is half the complex BW, and must be - * between 20e6 and 0.53e6. */ - double bbbw = _baseband_bw / 2.0; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 20e6 and 0.54e6. + * Max filter BW is 100 MHz. 100 / 5 = 20 + * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */ if (bbbw > 20e6) { bbbw = 20e6; - } else if (bbbw < 0.53e6) { - bbbw = 0.53e6; + } else if (bbbw < 0.54e6) { + bbbw = 0.54e6; } double bbbw_mhz = bbbw / 1e6; @@ -456,13 +513,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter() _io_iface->poke8(0x0d2, reg0d2); _io_iface->poke8(0x0d1, reg0d1); _io_iface->poke8(0x0d0, reg0d0); + + return bbbw; } /* Calibrate the RX TIAs. * * Note that the values in the TIA register, after calibration, vary with - * the RX gain settings. */ -void ad9361_device_t::_calibrate_rx_TIAs() + * the RX gain settings. + * We do not really program the BW here. Most settings are taken form the BB LPF registers + * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */ +double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw) { boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F; boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F; @@ -473,13 +534,21 @@ void ad9361_device_t::_calibrate_rx_TIAs() boost::uint8_t reg1de = 0x00; boost::uint8_t reg1df = 0x00; - /* For calibration, baseband BW is half the complex BW, and must be - * between 28e6 and 0.2e6. */ - double bbbw = _baseband_bw / 2.0; - if (bbbw > 20e6) { - bbbw = 20e6; - } else if (bbbw < 0.20e6) { - bbbw = 0.20e6; + double bbbw = req_rfbw / 2.0; + + if(bbbw > _baseband_bw / 2.0) + { + UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; + bbbw = _baseband_bw / 2.0; + } + + /* Baseband BW must be between 28e6 and 0.4e6. + * Max filter BW is 70 MHz. 70 / 2.5 = 28 + * Min filter BW is 1 MHz. 1 / 2.5 = 0.4*/ + if (bbbw > 28e6) { + bbbw = 28e6; + } else if (bbbw < 0.40e6) { + bbbw = 0.40e6; } double ceil_bbbw_mhz = std::ceil(bbbw / 1e6); @@ -520,6 +589,8 @@ void ad9361_device_t::_calibrate_rx_TIAs() _io_iface->poke8(0x1df, reg1df); _io_iface->poke8(0x1dc, reg1dc); _io_iface->poke8(0x1de, reg1de); + + return bbbw; } /* Setup the AD9361 ADC. @@ -651,11 +722,12 @@ void ad9361_device_t::_setup_adc() } /* Calibrate the baseband DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function! */ + * Disables tracking + */ void ad9361_device_t::_calibrate_baseband_dc_offset() { + _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag + _io_iface->poke8(0x193, 0x3f); // Calibration settings _io_iface->poke8(0x190, 0x0f); // Set tracking coefficient //write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift @@ -675,9 +747,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset() } /* Calibrate the RF DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function. */ + * Disables tracking + */ void ad9361_device_t::_calibrate_rf_dc_offset() { /* Some settings are frequency-dependent. */ @@ -692,7 +763,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset() } _io_iface->poke8(0x185, 0x20); // RF DC Offset wait count - _io_iface->poke8(0x18b, 0x83); + _io_iface->poke8(0x18b, 0x83); // Disable tracking _io_iface->poke8(0x189, 0x30); /* Run the calibration! */ @@ -706,30 +777,70 @@ void ad9361_device_t::_calibrate_rf_dc_offset() count++; boost::this_thread::sleep(boost::posix_time::milliseconds(50)); } + + _io_iface->poke8(0x18b, 0x8d); // Enable RF DC tracking +} + +void ad9361_device_t::_configure_bb_dc_tracking() +{ + if (_use_dc_offset_tracking) + _io_iface->poke8(0x18b, 0xad); // Enable BB tracking + else + _io_iface->poke8(0x18b, 0x8d); // Disable BB tracking } -/* Start the RX quadrature calibration. +void ad9361_device_t::_configure_rx_iq_tracking() +{ + if (_use_iq_balance_tracking) + _io_iface->poke8(0x169, 0xcf); // Enable Rx IQ tracking + else + _io_iface->poke8(0x169, 0xc0); // Disable Rx IQ tracking +} + +/* Single shot Rx quadrature calibration * - * Note that we are using AD9361's 'tracking' feature for RX quadrature - * calibration, so once it starts it continues to free-run during operation. - * It should be re-run for large frequency changes. */ + * Procedure documented in "AD9361 Calibration Guide". Prior to calibration, + * state should be set to ALERT, FDD, and Dual Synth Mode. Rx quadrature + * tracking will be disabled, so run before or instead of enabling Rx + * quadrature tracking. + */ void ad9361_device_t::_calibrate_rx_quadrature() { /* Configure RX Quadrature calibration settings. */ _io_iface->poke8(0x168, 0x03); // Set tone level for cal _io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal _io_iface->poke8(0x16a, 0x75); // Set Kexp phase - _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude - _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode - _io_iface->poke8(0x18b, 0xad); + _io_iface->poke8(0x16b, 0x95); // Set Kexp amplitude + _io_iface->poke8(0x057, 0x33); // Power down Tx mixer + _io_iface->poke8(0x169, 0xc0); // Disable tracking and free run mode + + /* Place Tx LO within passband of Rx spectrum */ + double current_tx_freq = _tx_freq; + _tune_helper(TX, _rx_freq + _rx_bb_lp_bw / 2.0); + + size_t count = 0; + _io_iface->poke8(0x016, 0x20); + while (_io_iface->peek8(0x016) & 0x20) { + if (count > 100) { + throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure"); + break; + } + count++; + boost::this_thread::sleep(boost::posix_time::milliseconds(5)); + } + + _io_iface->poke8(0x057, 0x30); // Re-enable Tx mixers + + _tune_helper(TX, current_tx_freq); } -/* TX quadtrature calibration routine. +/* TX quadrature calibration routine. * * The TX quadrature needs to be done twice, once for each TX chain, with * only one register change in between. Thus, this function enacts the * calibrations, and it is called from calibrate_tx_quadrature. */ void ad9361_device_t::_tx_quadrature_cal_routine() { + /* This is a weird process, but here is how it works: * 1) Read the calibrated NCO frequency bits out of 0A3. * 2) Write the two bits to the RX NCO freq part of 0A0. @@ -765,7 +876,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { /* The gain table index used for calibration must be adjusted for the * mid-table to get a TIA index = 1 and LPF index = 0. */ - if ((_rx_freq >= 1300e6) && (_rx_freq < 4000e6)) { + if (_rx_freq < 1300e6) { _io_iface->poke8(0x0aa, 0x22); // Cal gain table index } else { _io_iface->poke8(0x0aa, 0x25); // Cal gain table index @@ -774,12 +885,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { _io_iface->poke8(0x0a4, 0xf0); // Cal setting conut _io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode) - /* First, calibrate the baseband DC offset. */ - _calibrate_baseband_dc_offset(); - - /* Second, calibrate the RF DC offset. */ - _calibrate_rf_dc_offset(); - /* Now, calibrate the TX quadrature! */ size_t count = 0; _io_iface->poke8(0x016, 0x10); @@ -794,9 +899,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() { } /* Run the TX quadrature calibration. - * - * Note that from within this function we are also triggering the baseband - * and RF DC calibrations. */ + */ void ad9361_device_t::_calibrate_tx_quadrature() { /* Make sure we are, in fact, in the ALERT state. If not, something is @@ -880,7 +983,7 @@ void ad9361_device_t::_program_mixer_gm_subtable() void ad9361_device_t::_program_gain_table() { /* Figure out which gain table we should be using for our current * frequency band. */ - boost::uint8_t (*gain_table)[5] = NULL; + boost::uint8_t (*gain_table)[3] = NULL; boost::uint8_t new_gain_table; if (_rx_freq < 1300e6) { gain_table = gain_table_sub_1300mhz; @@ -911,9 +1014,9 @@ void ad9361_device_t::_program_gain_table() { boost::uint8_t index = 0; for (; index < 77; index++) { _io_iface->poke8(0x130, index); - _io_iface->poke8(0x131, gain_table[index][1]); - _io_iface->poke8(0x132, gain_table[index][2]); - _io_iface->poke8(0x133, gain_table[index][3]); + _io_iface->poke8(0x131, gain_table[index][0]); + _io_iface->poke8(0x132, gain_table[index][1]); + _io_iface->poke8(0x133, gain_table[index][2]); _io_iface->poke8(0x137, 0x1E); _io_iface->poke8(0x134, 0x00); _io_iface->poke8(0x134, 0x00); @@ -939,28 +1042,58 @@ void ad9361_device_t::_program_gain_table() { /* Setup gain control registers. * - * This really only needs to be done once, at initialization. */ -void ad9361_device_t::_setup_gain_control() + * This really only needs to be done once, at initialization. + * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */ +void ad9361_device_t::_setup_gain_control(bool agc) { - _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select - _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl - _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size - _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index - _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time - _io_iface->poke8(0x100, 0x6F); // Max Digital Gain - _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold - _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold - _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold - _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold - _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index - _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index - _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index - _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index - _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index - _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index - _io_iface->poke8(0x114, 0x30); // Low Power Threshold - _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit - _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + /* The AGC mode configuration should be good for all cases. + * However, non AGC configuration still used for backward compatibility. */ + if (agc) { + /*mode select bits must be written before hand!*/ + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x101, 0x0A); // Max Digital Gain + _io_iface->poke8(0x103, 0x08); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x106, 0x22); // Max Digital Gain + _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x31); + _io_iface->poke8(0x111, 0x0A); + _io_iface->poke8(0x11A, 0x1C); + _io_iface->poke8(0x120, 0x0C); + _io_iface->poke8(0x121, 0x44); + _io_iface->poke8(0x122, 0x44); + _io_iface->poke8(0x123, 0x11); + _io_iface->poke8(0x124, 0xF5); + _io_iface->poke8(0x125, 0x3B); + _io_iface->poke8(0x128, 0x03); + _io_iface->poke8(0x129, 0x56); + _io_iface->poke8(0x12A, 0x22); + } else { + _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select + _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size + _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index + _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time + _io_iface->poke8(0x100, 0x6F); // Max Digital Gain + _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold + _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold + _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold + _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold + _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index + _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index + _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index + _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index + _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index + _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index + _io_iface->poke8(0x114, 0x30); // Low Power Threshold + _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit + _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control + } } /* Setup the RX or TX synthesizers. @@ -1153,16 +1286,16 @@ double ad9361_device_t::_tune_helper(direction_t direction, const double value) /* Set band-specific settings. */ if (value < _client_params->get_band_edge(AD9361_RX_BAND0)) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x30; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x30; // Port C, balanced } else if ((value >= _client_params->get_band_edge(AD9361_RX_BAND0)) && (value < _client_params->get_band_edge(AD9361_RX_BAND1))) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x0C; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x0C; // Port B, balanced } else if ((value >= _client_params->get_band_edge(AD9361_RX_BAND1)) && (value <= 6e9)) { - _regs.inputsel = (_regs.inputsel & 0xC0) | 0x03; + _regs.inputsel = (_regs.inputsel & 0xC0) | 0x03; // Port A, balanced } else { throw uhd::runtime_error("[ad9361_device_t] [_tune_helper] INVALID_CODE_PATH"); } @@ -1257,6 +1390,7 @@ double ad9361_device_t::_setup_rates(const double rate) int divfactor = 0; _tfir_factor = 0; _rfir_factor = 0; + if (rate < 0.33e6) { // RX1 + RX2 enabled, 3, 2, 2, 4 _regs.rxfilt = B8(11101111); @@ -1397,8 +1531,8 @@ void ad9361_device_t::initialize() _regs.bbftune_mode = 0x1e; /* Initialize private VRQ fields. */ - _rx_freq = 0.0; - _tx_freq = 0.0; + _rx_freq = DEFAULT_RX_FREQ; + _tx_freq = DEFAULT_TX_FREQ; _req_rx_freq = 0.0; _req_tx_freq = 0.0; _baseband_bw = 0.0; @@ -1412,6 +1546,18 @@ void ad9361_device_t::initialize() _rx2_gain = 0; _tx1_gain = 0; _tx2_gain = 0; + _use_dc_offset_tracking = true; + _use_iq_balance_tracking = true; + _rx1_agc_mode = GAIN_MODE_SLOW_AGC; + _rx2_agc_mode = GAIN_MODE_SLOW_AGC; + _rx1_agc_enable = false; + _rx2_agc_enable = false; + _rx_analog_bw = 0; + _tx_analog_bw = 0; + _rx_tia_lp_bw = 0; + _tx_sec_lp_bw = 0; + _rx_bb_lp_bw = 0; + _tx_bb_lp_bw = 0; /* Reset the device. */ _io_iface->poke8(0x000, 0x01); @@ -1490,7 +1636,6 @@ void ad9361_device_t::initialize() _io_iface->poke8(0x019, 0x00); // AuxDAC2 Word[9:2] _io_iface->poke8(0x01A, 0x00); // AuxDAC1 Config and Word[1:0] _io_iface->poke8(0x01B, 0x00); // AuxDAC2 Config and Word[1:0] - _io_iface->poke8(0x022, 0x4A); // Invert Bypassed LNA _io_iface->poke8(0x023, 0xFF); // AuxDAC Manaul/Auto Control _io_iface->poke8(0x026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select _io_iface->poke8(0x030, 0x00); // AuxDAC1 Rx Delay @@ -1498,10 +1643,18 @@ void ad9361_device_t::initialize() _io_iface->poke8(0x032, 0x00); // AuxDAC2 Rx Delay _io_iface->poke8(0x033, 0x00); // AuxDAC2 Tx Delay + /* LNA bypass polarity inversion + * According to the register map, we should invert the bypass path to + * match LNA phase. Extensive testing, however, shows otherwise and that + * to align bypass and LNA phases, the bypass inversion switch should be + * turned off. + */ + _io_iface->poke8(0x022, 0x0A); + /* Setup AuxADC */ _io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset) _io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window) - _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) + _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual Measure) _io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation) _io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div) _io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) @@ -1550,23 +1703,34 @@ void ad9361_device_t::initialize() _calibrate_synth_charge_pumps(); - _tune_helper(RX, 800e6); - _tune_helper(TX, 850e6); + _tune_helper(RX, _rx_freq); + _tune_helper(TX, _tx_freq); _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); - _calibrate_tx_quadrature(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_rx_quadrature(); + /* + * Rx BB DC and IQ tracking are both disabled by calibration at this + * point. Only issue commands if tracking needs to be turned on. + */ + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + + _last_rx_cal_freq = _rx_freq; + _last_tx_cal_freq = _tx_freq; + // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { case AD9361_DDR_FDD_LVCMOS: { @@ -1680,19 +1844,30 @@ double ad9361_device_t::set_clock_rate(const double req_rate) _program_mixer_gm_subtable(); _program_gain_table(); - _setup_gain_control(); + _setup_gain_control(false); _reprogram_gains(); - _calibrate_baseband_rx_analog_filter(); - _calibrate_baseband_tx_analog_filter(); - _calibrate_rx_TIAs(); - _calibrate_secondary_tx_filter(); + set_bw_filter(RX, _baseband_bw); + set_bw_filter(TX, _baseband_bw); _setup_adc(); - _calibrate_tx_quadrature(); + _calibrate_baseband_dc_offset(); + _calibrate_rf_dc_offset(); _calibrate_rx_quadrature(); + /* + * Rx BB DC and IQ tracking are both disabled by calibration at this + * point. Only issue commands if tracking needs to be turned on. + */ + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + + _last_rx_cal_freq = _rx_freq; + _last_tx_cal_freq = _tx_freq; + // cals done, set PPORT config switch (_client_params->get_digital_interface_mode()) { case AD9361_DDR_FDD_LVCMOS: { @@ -1793,6 +1968,14 @@ void ad9361_device_t::set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) _io_iface->poke8(0x002, _regs.txfilt); _io_iface->poke8(0x003, _regs.rxfilt); + /* + * Last unconditional Tx calibration point. Any later Tx calibration will + * require user intervention (currently triggered by tuning difference that + * is > 100 MHz). Late calibration provides better performance. + */ + if (tx1 | tx2) + _calibrate_tx_quadrature(); + /* Put back into FDD state if necessary */ if (set_back_to_fdd) _io_iface->poke8(0x014, 0x21); @@ -1808,17 +1991,18 @@ void ad9361_device_t::set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) double ad9361_device_t::tune(direction_t direction, const double value) { boost::lock_guard<boost::recursive_mutex> lock(_mutex); + double last_cal_freq; if (direction == RX) { if (freq_is_nearly_equal(value, _req_rx_freq)) { return _rx_freq; } - + last_cal_freq = _last_rx_cal_freq; } else if (direction == TX) { if (freq_is_nearly_equal(value, _req_tx_freq)) { return _tx_freq; } - + last_cal_freq = _last_tx_cal_freq; } else { throw uhd::runtime_error("[ad9361_device_t] [tune] INVALID_CODE_PATH"); } @@ -1843,9 +2027,30 @@ double ad9361_device_t::tune(direction_t direction, const double value) /* Update the gain settings. */ _reprogram_gains(); - /* Run the calibration algorithms. */ - _calibrate_tx_quadrature(); - _calibrate_rx_quadrature(); + /* + * Only run the following calibrations if we are more than 100MHz away + * from the previous Tx or Rx calibration point. Leave out single shot + * Rx quadrature unless Rx quad-cal is disabled. + */ + if (std::abs(last_cal_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) { + /* Run the calibration algorithms. */ + if (direction == RX) { + _calibrate_rf_dc_offset(); + if (!_use_iq_balance_tracking) + _calibrate_rx_quadrature(); + if (_use_dc_offset_tracking) + _configure_bb_dc_tracking(); + + _last_rx_cal_freq = tune_freq; + } else { + _calibrate_tx_quadrature(); + _last_tx_cal_freq = tune_freq; + } + + /* Rx IQ tracking can be disabled on Rx or Tx re-calibration */ + if (_use_iq_balance_tracking) + _configure_rx_iq_tracking(); + } /* If we were in the FDD state, return it now. */ if (not_in_alert) { @@ -1922,7 +2127,7 @@ double ad9361_device_t::set_gain(direction_t direction, chain_t chain, const dou } } -void ad9361_device_t::output_test_tone() +void ad9361_device_t::output_test_tone() // On RF side! { boost::lock_guard<boost::recursive_mutex> lock(_mutex); /* Output a 480 kHz tone at 800 MHz */ @@ -1932,6 +2137,12 @@ void ad9361_device_t::output_test_tone() _io_iface->poke8(0x3FE, 0x3F); } +void ad9361_device_t::digital_test_tone(bool enb) // Digital output +{ + boost::lock_guard<boost::recursive_mutex> lock(_mutex); + _io_iface->poke8(0x3F4, 0x02 | (enb ? 0x01 : 0x00)); +} + void ad9361_device_t::data_port_loopback(const bool loopback_enabled) { boost::lock_guard<boost::recursive_mutex> lock(_mutex); @@ -1960,4 +2171,663 @@ double ad9361_device_t::get_rssi(chain_t chain) return rssi; } +/* + * Returns the reading of the internal temperature sensor. + * One point calibration of the sensor was done according to datasheet + * leading to the given default constant correction factor. + */ +double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout) +{ + //set 0x01D[0] to 1 to disable AuxADC GPIO reading + boost::uint8_t tmp = 0; + tmp = _io_iface->peek8(0x01D); + _io_iface->poke8(0x01D, (tmp | 0x01)); + _io_iface->poke8(0x00B, 0); //set offset to 0 + + _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1] + boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::time_duration elapsed; + //wait for valid data (toggle of bit 1 in 0x00C) + while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) { + boost::this_thread::sleep(boost::posix_time::microseconds(100)); + elapsed = boost::posix_time::microsec_clock::local_time() - start_time; + if(elapsed.total_milliseconds() > (timeout*1000)) + { + throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature"); + } + } + _io_iface->poke8(0x00C, 0x00); //clear read flag + + boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature. + double tmp_temp = temp/1.140f; //according to ADI driver + tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration. + + return tmp_temp; +} + +double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples) +{ + double d_temp = 0; + for(size_t i = 0; i < num_samples; i++) { + double tmp_temp = _get_temperature(cal_offset); + d_temp += (tmp_temp/num_samples); + } + return d_temp; +} + +/* + * Enable/Disable DC offset tracking + * + * Only disable BB tracking while leaving static RF and BB DC calibrations enabled. + * According to correspondance from ADI, turning off Rx BB DC tracking clears the + * correction words so we don't need to be concerned with leaving the calibration + * in a bad state upon disabling. Testing also confirms this behavior. + * + * Note that Rx IQ tracking does not show similar state clearing behavior when + * disabled. + */ +void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on) +{ + if (direction == RX) { + _use_dc_offset_tracking = on; + _configure_bb_dc_tracking(); + } else { + throw uhd::runtime_error("[ad9361_device_t] [set_dc_offset_auto] Tx DC tracking not supported"); + } +} + +/* + * Enable/Disable IQ balance tracking + * + * Run static Rx quadrature calibration after disabling quadrature tracking. + * This avoids the situation where a user might disable tracking when the loop + * is in a confused state (e.g. at or near saturation). Otherwise, the + * calibration setting could be forced to and left in a bad state. + */ +void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on) +{ + if (direction == RX) { + _use_iq_balance_tracking = on; + _configure_rx_iq_tracking(); + if (!on) { + _io_iface->poke8(0x014, 0x05); // ALERT mode + _calibrate_rx_quadrature(); + _io_iface->poke8(0x014, 0x21); // FDD mode + } + } else { + throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] Tx IQ tracking not supported"); + } +} + +/* Sets the RX gain mode to be used. + * If a transition from an AGC to an non AGC mode occurs (or vice versa) + * the gain configuration will be reloaded. */ +void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode) +{ + boost::uint8_t gain_mode_reg = 0; + boost::uint8_t gain_mode_prev = 0; + boost::uint8_t gain_mode_bits_pos = 0; + + gain_mode_reg = _io_iface->peek8(0x0FA); + gain_mode_prev = (gain_mode_reg & 0x0F); + + if (chain == CHAIN_1) { + gain_mode_bits_pos = 0; + } else if (chain == CHAIN_2) { + gain_mode_bits_pos = 2; + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } + + gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits + switch (gain_mode) { + case GAIN_MODE_MANUAL: + //leave bits cleared + break; + case GAIN_MODE_SLOW_AGC: + gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos)); + break; + case GAIN_MODE_FAST_AGC: + gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos)); + break; + default: + throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist"); + } + _io_iface->poke8(0x0FA, gain_mode_reg); + boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA); + gain_mode_status = (gain_mode_status & 0x0F); + /*Check if gain mode configuration needs to be reprogrammed*/ + if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) { + if (gain_mode_status == 0) { + /*load manual mode config*/ + _setup_gain_control(false); + } else { + /*load agc mode config*/ + _setup_gain_control(true); + } + } +} + +void ad9361_device_t::set_agc(chain_t chain, bool enable) +{ + if(chain == CHAIN_1) { + _rx1_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx1_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else if (chain == CHAIN_2){ + _rx2_agc_enable = enable; + if(enable) { + _setup_agc(chain, _rx2_agc_mode); + } else { + _setup_agc(chain, GAIN_MODE_MANUAL); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode) +{ + if(chain == CHAIN_1) { + _rx1_agc_mode = gain_mode; + if(_rx1_agc_enable) { + _setup_agc(chain, _rx1_agc_mode); + } + } else if(chain == CHAIN_2){ + _rx2_agc_mode = gain_mode; + if(_rx2_agc_enable) { + _setup_agc(chain, _rx2_agc_mode); + } + } else + { + throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); + } +} + +std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction) +{ + std::vector<std::string> ret; + if(direction == RX) { + for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) { + ret.push_back(it->first); + } + } else if (direction == TX) + { + for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) { + ret.push_back(it->first); + } + } + return ret; +} + +filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name) +{ + if(direction == RX) { + if (not _rx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _rx_filters[name].get(direction, chain); + } else if (direction == TX) { + if (not _tx_filters[name].get) + { + throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); + } + return _tx_filters[name].get(direction, chain); + } + + throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter."); +} + +void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter) +{ + + if(direction == RX) { + if(not _rx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _rx_filters[name].set(direction, chain, filter); + } else if (direction == TX) { + if(not _tx_filters[name].set) + { + throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); + } + _tx_filters[name].set(direction, chain, filter); + } + +} + +double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw) +{ + //both low pass filters are programmed to the same bw. However, their cutoffs will differ. + //Together they should create the requested bb bw. + double set_analog_bb_bw = 0; + if(direction == RX) + { + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw + _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw); + _rx_analog_bw = _rx_bb_lp_bw; + set_analog_bb_bw = _rx_analog_bw; + } else { + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw); + _tx_analog_bw = _tx_bb_lp_bw; + set_analog_bb_bw = _tx_analog_bw; + } + return (2.0 * set_analog_bb_bw); +} + +void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps) +{ + size_t num_taps = taps.size(); + size_t num_taps_avail = _get_num_fir_taps(direction); + if(num_taps == num_taps_avail) + { + boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]); + for (size_t i = 0; i < num_taps_avail; i++) + { + coeffs[i] = boost::uint16_t(taps[i]); + } + _program_fir_filter(direction, chain, num_taps_avail, coeffs.get()); + } else if(num_taps < num_taps_avail){ + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients."); + } else { + throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients."); + } +} + +size_t ad9361_device_t::_get_num_fir_taps(direction_t direction) +{ + boost::uint8_t num = 0; + if(direction == RX) + num = _io_iface->peek8(0x0F5); + else + num = _io_iface->peek8(0x065); + num = ((num >> 5) & 0x07); + return ((num + 1) * 16); +} + +size_t ad9361_device_t::_get_fir_dec_int(direction_t direction) +{ + boost::uint8_t dec_int = 0; + if(direction == RX) + dec_int = _io_iface->peek8(0x003); + else + dec_int = _io_iface->peek8(0x002); + /* + * 0 = dec/int by 1 and bypass filter + * 1 = dec/int by 1 + * 2 = dec/int by 2 + * 3 = dec/int by 4 */ + dec_int = (dec_int & 0x03); + if(dec_int == 3) + { + return 4; + } + return dec_int; +} + +std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain) +{ + int base; + size_t num_taps = _get_num_fir_taps(direction); + boost::uint8_t config; + boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; + config = reg_numtaps | 0x02; //start the programming clock + + if(chain == CHAIN_1) + { + config = config | (1 << 3); + } else if (chain == CHAIN_2){ + config = config | (1 << 4); + } else { + throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously"); + } + + if(direction == RX) + { + base = 0xF0; + } else { + base = 0x60; + } + + _io_iface->poke8(base+5,config); + + std::vector<boost::int16_t> taps; + boost::uint8_t lower_val; + boost::uint8_t higher_val; + boost::uint16_t coeff; + for(size_t i = 0;i < num_taps;i++) + { + _io_iface->poke8(base,0x00+i); + lower_val = _io_iface->peek8(base+3); + higher_val = _io_iface->peek8(base+4); + coeff = ((higher_val << 8) | lower_val); + taps.push_back(boost::int16_t(coeff)); + } + + config = (config & (~(1 << 1))); //disable filter clock + _io_iface->poke8(base+5,config); + return taps; +} + +/* + * Returns either RX TIA LPF or TX Secondary LPF + * depending on the direction. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction) +{ + double cutoff = 0; + + if(direction == RX) + { + cutoff = 2.5 * _rx_tia_lp_bw; + } else { + cutoff = 5 * _tx_sec_lp_bw; + } + + filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20)); + return lp; +} + +/* + * Returns RX/TX BB LPF. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction) +{ + double cutoff = 0; + if(direction == RX) + { + cutoff = 1.4 * _rx_bb_lp_bw; + } else { + cutoff = 1.6 * _tx_bb_lp_bw; + } + + filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60)); + return bb_lp; +} + +/* + * For RX direction the DEC3 is returned. + * For TX direction the INT3 is returned. */ +filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale; + size_t dec = 0; + size_t interpol = 0; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + std::string name; + boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55}; + boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36}; + std::vector<boost::int16_t> taps; + + filter_info_base::sptr ret; + + if(direction == RX) + { + full_scale = 16384; + dec = 3; + interpol = 1; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + + } else { + full_scale = 8192; + dec = 1; + interpol = 3; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3 + { + rate /= 3; + } + + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return ret; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1}; + boost::int16_t taps_array_tx[] = {1, 2, 1}; + std::vector<boost::int16_t> taps; + + if(direction == RX) + { + full_scale = 16; + dec = 2; + + enable = _io_iface->peek8(0x003); + enable = ((enable >> 4) & 0x03); + taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + } else { + full_scale = 2; + interpol = 2; + + boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); + use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); + if(use_dac_clk_div == 1) + { + rate = rate / 2; + } + + enable = _io_iface->peek8(0x002); + enable = ((enable >> 4) & 0x03); + if(enable == 1) + { + rate /= 2; + } + taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = _adcclock_freq; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9}; + std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) ); + + digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction)); + digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction)); + + if(direction == RX) + { + full_scale = 256; + dec = 2; + enable = _io_iface->peek8(0x003); + } else { + full_scale = 128; + interpol = 2; + enable = _io_iface->peek8(0x002); + } + + enable = ((enable >> 3) & 0x01); + + if(!(hb_3->is_bypassed())) + { + if(direction == RX) + { + rate = hb_3->get_output_rate(); + }else if (direction == TX) { + rate = hb_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } else { //else dec3/int3 or none of them is used. + if(direction == RX) + { + rate = dec_int_3->get_output_rate(); + }else if (direction == TX) { + rate = dec_int_3->get_input_rate(); + if(enable) + { + rate /= 2; + } + } + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction) +{ + boost::uint8_t enable = 0; + double rate = 0; + double full_scale = 0; + size_t dec = 1; + size_t interpol = 1; + filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + + std::vector<boost::int16_t> taps; + boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8}; + boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53}; + + digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction)); + + if(direction == RX) + { + full_scale = 2048; + dec = 2; + enable = _io_iface->peek8(0x003); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_output_rate(); + taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) ); + } else if (direction == TX) { + full_scale = 8192; + interpol = 2; + enable = _io_iface->peek8(0x002); + enable = ((enable >> 2) & 0x01); + rate = hb_2->get_input_rate(); + if(enable) + { + rate /= 2; + } + taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) ); + } + + filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps)); + return hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain) +{ + double rate = 0; + size_t dec = 1; + size_t interpol = 1; + size_t max_num_taps = 128; + boost::uint8_t enable = 1; + + digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction)); + + if(direction == RX) + { + dec = _get_fir_dec_int(direction); + if(dec == 0) + { + enable = 0; + dec = 1; + } + interpol = 1; + rate = hb_1->get_output_rate(); + }else if (direction == TX) { + interpol = _get_fir_dec_int(direction); + if(interpol == 0) + { + enable = 0; + interpol = 1; + } + dec = 1; + rate = hb_1->get_input_rate(); + if(enable) + { + rate /= interpol; + } + } + max_num_taps = _get_num_fir_taps(direction); + + filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain))); + + return fir; +} + +void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter) +{ + digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter); + //only write taps. Ignore everything else for now + _set_fir_taps(direction, channel, fir->get_taps()); +} + +/* + * If BW of one of the analog filters gets overwritten manually, + * _tx_analog_bw and _rx_analog_bw are not valid any more! + * For useful data in those variables set_bw_filter method should be used + */ +void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value + _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value + _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6); + } +} + +void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter) +{ + analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); + double bw = lpf->get_cutoff(); + if(direction == RX) + { + //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value + _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw + + } else { + //remember: this function takes rf bw as its input and calibrates to 5 x the given value + _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5); + } +} + }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 71ce78da7..66bc2e8b9 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_DEVICE_H @@ -8,6 +21,14 @@ #include <ad9361_client.h> #include <boost/noncopyable.hpp> #include <boost/thread/recursive_mutex.hpp> +#include <uhd/types/filters.hpp> +#include <uhd/types/sensors.hpp> +#include <complex> +#include <vector> +#include <map> +#include "boost/assign.hpp" +#include "boost/bind.hpp" +#include "boost/function.hpp" namespace uhd { namespace usrp { @@ -15,10 +36,41 @@ class ad9361_device_t : public boost::noncopyable { public: enum direction_t { RX, TX }; - enum chain_t { CHAIN_1, CHAIN_2 }; + enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC}; + enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH }; ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) : - _client_params(client), _io_iface(io_iface) {} + _client_params(client), _io_iface(io_iface) { + + /* + * This Boost.Assign to_container() workaround is necessary because STL containers + * apparently confuse newer versions of MSVC. + * + * Source: http://www.boost.org/doc/libs/1_55_0/libs/assign/doc/#portability + */ + + _rx_filters = (boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_rx_filters); + + _tx_filters = (boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) + ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), + boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) + ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) + ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) + ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) + ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) + ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), + boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_tx_filters); + } /* Initialize the AD9361 codec. */ void initialize(); @@ -63,27 +115,66 @@ public: /* Make AD9361 output its test tone. */ void output_test_tone(); + void digital_test_tone(bool enb); // Digital output + /* Turn on/off AD9361's TX port --> RX port loopback. */ void data_port_loopback(const bool loopback_enabled); /* Read back the internal RSSI measurement data. */ double get_rssi(chain_t chain); + /*! Read the internal temperature sensor + *\param calibrate return raw sensor readings or apply calibration factor. + *\param num_samples number of measurements to average over + */ + double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3); + + /* Turn on/off AD9361's RX DC offset correction */ + void set_dc_offset_auto(direction_t direction, const bool on); + + /* Turn on/off AD9361's RX IQ imbalance correction */ + void set_iq_balance_auto(direction_t direction, const bool on); + + /* Configure AD9361's AGC module to use either fast or slow AGC mode. */ + void set_agc_mode(chain_t chain, gain_mode_t gain_mode); + + /* Enable AD9361's AGC gain mode. */ + void set_agc(chain_t chain, bool enable); + + /* Set bandwidth of AD9361's analog LP filters. + * Bandwidth should be RF bandwidth */ + double set_bw_filter(direction_t direction, const double rf_bw); + + /* + * Filter API implementation + * */ + filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name); + + void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter); + + std::vector<std::string> get_filter_names(direction_t direction); + //Constants static const double AD9361_MAX_GAIN; static const double AD9361_MAX_CLOCK_RATE; + static const double AD9361_CAL_VALID_WINDOW; static const double AD9361_RECOMMENDED_MAX_BANDWIDTH; + static const double DEFAULT_RX_FREQ; + static const double DEFAULT_TX_FREQ; private: //Methods void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs); void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation); void _setup_rx_fir(size_t num_taps, boost::int32_t decimation); + void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs); + void _setup_tx_fir(size_t num_taps); + void _setup_rx_fir(size_t num_taps); void _calibrate_lock_bbpll(); void _calibrate_synth_charge_pumps(); - double _calibrate_baseband_rx_analog_filter(); - double _calibrate_baseband_tx_analog_filter(); - void _calibrate_secondary_tx_filter(); - void _calibrate_rx_TIAs(); + double _calibrate_baseband_rx_analog_filter(double rfbw); + double _calibrate_baseband_tx_analog_filter(double rfbw); + double _calibrate_secondary_tx_filter(double rfbw); + double _calibrate_rx_TIAs(double rfbw); void _setup_adc(); void _calibrate_baseband_dc_offset(); void _calibrate_rf_dc_offset(); @@ -92,12 +183,30 @@ private: //Methods void _calibrate_tx_quadrature(); void _program_mixer_gm_subtable(); void _program_gain_table(); - void _setup_gain_control(); + void _setup_gain_control(bool use_agc); void _setup_synth(direction_t direction, double vcorate); double _tune_bbvco(const double rate); void _reprogram_gains(); double _tune_helper(direction_t direction, const double value); double _setup_rates(const double rate); + double _get_temperature(const double cal_offset, const double timeout = 0.1); + void _configure_bb_dc_tracking(); + void _configure_rx_iq_tracking(); + void _setup_agc(chain_t chain, gain_mode_t gain_mode); + void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps); + std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain); + size_t _get_num_fir_taps(direction_t direction); + size_t _get_fir_dec_int(direction_t direction); + filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction); + filter_info_base::sptr _get_filter_lp_bb(direction_t direction); + filter_info_base::sptr _get_filter_dec_int_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_3(direction_t direction); + filter_info_base::sptr _get_filter_hb_2(direction_t direction); + filter_info_base::sptr _get_filter_hb_1(direction_t direction); + filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain); + void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter); + void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter); + void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter); private: //Members typedef struct { @@ -110,11 +219,30 @@ private: //Members boost::uint8_t bbftune_mode; } chip_regs_t; + struct filter_query_helper + { + filter_query_helper( + boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get, + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set + ) : get(p_get), set(p_set) { } + + filter_query_helper(){ } + + boost::function<filter_info_base::sptr (direction_t, chain_t)> get; + boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set; + }; + + std::map<std::string, filter_query_helper> _rx_filters; + std::map<std::string, filter_query_helper> _tx_filters; + //Interfaces ad9361_params::sptr _client_params; ad9361_io::sptr _io_iface; //Intermediate state double _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; + double _last_rx_cal_freq, _last_tx_cal_freq; + double _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw; + double _rx_tia_lp_bw, _tx_sec_lp_bw; //! Current baseband sampling rate (this is the actual rate the device is // is running at) double _baseband_bw; @@ -129,10 +257,14 @@ private: //Members double _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain; boost::int32_t _tfir_factor; boost::int32_t _rfir_factor; + gain_mode_t _rx1_agc_mode, _rx2_agc_mode; + bool _rx1_agc_enable, _rx2_agc_enable; //Register soft-copies chip_regs_t _regs; //Synchronization boost::recursive_mutex _mutex; + bool _use_dc_offset_tracking; + bool _use_iq_balance_tracking; }; }} //namespace diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h index a1a85a49a..97ff858fd 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_FILTER_TAPS_HPP diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h index 786029d6e..8cd958e23 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h @@ -1,5 +1,18 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_GAIN_TABLES_HPP @@ -7,91 +20,91 @@ #include <boost/cstdint.hpp> -boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, - {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, - {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, - {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, - {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, - {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, - {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_sub_1300mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, +{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, +{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, +{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, +{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, +{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, +{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, +{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, - {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, - {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, - {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, - {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, - {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, - {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, - {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, - {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, - {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, - {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, - {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, - {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, - {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, - {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, - {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, - {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, - {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, +{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, - {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, - {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, - {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, - {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, - {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, - {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, - {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, - {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, - {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, - {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, - {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, - {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, - {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, - {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, - {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, - {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, - {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, - {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, - {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, - {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, - {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, - {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, - {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, - {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, - {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, - {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, +{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, +{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, +{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, +{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, +{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, +{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, +{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, +{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, +{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; #endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h index cb320e1f4..8155aae7c 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h @@ -1,25 +1,38 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. // #ifndef INCLUDED_AD9361_SYNTH_LUT_HPP #define INCLUDED_AD9361_SYNTH_LUT_HPP -double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, - 11288000000, 11007000000, 10742000000, 10492000000, - 10258000000, 10036000000, 9827800000, 9631100000, - 9445300000, 9269800000, 9103600000, 8946300000, - 8797000000, 8655300000, 8520600000, 8392300000, - 8269900000, 8153100000, 8041400000, 7934400000, - 7831800000, 7733200000, 7638400000, 7547100000, - 7459000000, 7374000000, 7291900000, 7212400000, - 7135500000, 7061000000, 6988700000, 6918600000, - 6850600000, 6784600000, 6720500000, 6658200000, - 6597800000, 6539200000, 6482300000, 6427000000, - 6373400000, 6321400000, 6270900000, 6222000000, - 6174500000, 6128400000, 6083600000, 6040100000, - 5997700000}; +double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0, + 11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0, + 10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0, + 9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0, + 8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0, + 8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0, + 7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0, + 7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0, + 7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0, + 6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0, + 6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0, + 6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0, + 6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0, + 5997700000.0}; int synth_cal_lut[53][12] = { {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9}, {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9}, diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp new file mode 100644 index 000000000..8c8897803 --- /dev/null +++ b/host/lib/usrp/common/ad936x_manager.cpp @@ -0,0 +1,280 @@ +// +// Copyright 2015 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "ad936x_manager.hpp" +#include <uhd/utils/msg.hpp> +#include <boost/foreach.hpp> +#include <boost/functional/hash.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +/**************************************************************************** + * Default values + ***************************************************************************/ +const double ad936x_manager::DEFAULT_GAIN = 0; +const double ad936x_manager::DEFAULT_BANDWIDTH = 56e6; +const double ad936x_manager::DEFAULT_TICK_RATE = 16e6; +const double ad936x_manager::DEFAULT_FREQ = 100e6; // Hz +const uint32_t ad936x_manager::DEFAULT_DECIM = 128; +const uint32_t ad936x_manager::DEFAULT_INTERP = 128; +const bool ad936x_manager::DEFAULT_AUTO_DC_OFFSET = true; +const bool ad936x_manager::DEFAULT_AUTO_IQ_BALANCE = true; +const bool ad936x_manager::DEFAULT_AGC_ENABLE = false; + +class ad936x_manager_impl : public ad936x_manager +{ + public: + /************************************************************************ + * Structor + ***********************************************************************/ + ad936x_manager_impl( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends + ) : _codec_ctrl(codec_ctrl), + _n_frontends(n_frontends) + { + if (_n_frontends < 1 or _n_frontends > 2) { + throw uhd::runtime_error(str( + boost::format("AD936x device can only have either 1 or 2 frontends, not %d.") + % _n_frontends + )); + } + for (size_t i = 1; i <= _n_frontends; i++) { + _rx_frontends.push_back(str(boost::format("RX%d") % i)); + _tx_frontends.push_back(str(boost::format("TX%d") % i)); + } + } + + /************************************************************************ + * API Calls + ***********************************************************************/ + void init_codec() + { + BOOST_FOREACH(const std::string &rx_fe, _rx_frontends) { + _codec_ctrl->set_gain(rx_fe, DEFAULT_GAIN); + _codec_ctrl->set_bw_filter(rx_fe, DEFAULT_BANDWIDTH); + _codec_ctrl->tune(rx_fe, DEFAULT_FREQ); + _codec_ctrl->set_dc_offset_auto(rx_fe, DEFAULT_AUTO_DC_OFFSET); + _codec_ctrl->set_iq_balance_auto(rx_fe, DEFAULT_AUTO_IQ_BALANCE); + _codec_ctrl->set_agc(rx_fe, DEFAULT_AGC_ENABLE); + } + BOOST_FOREACH(const std::string &tx_fe, _tx_frontends) { + _codec_ctrl->set_gain(tx_fe, DEFAULT_GAIN); + _codec_ctrl->set_bw_filter(tx_fe, DEFAULT_BANDWIDTH); + _codec_ctrl->tune(tx_fe, DEFAULT_FREQ); + } + } + + void loopback_self_test( + wb_iface::sptr iface, + wb_iface::wb_addr_type codec_idle_addr, + wb_iface::wb_addr_type codec_readback_addr + ) { + _codec_ctrl->data_port_loopback(true); + UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; + UHD_ASSERT_THROW(bool(iface)); + size_t hash = size_t(time(NULL)); + for (size_t i = 0; i < 100; i++) + { + boost::hash_combine(hash, i); + const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; + iface->poke32(codec_idle_addr, word32); + // We do 2 peeks so we have enough idleness for loopback to propagate + iface->peek64(codec_readback_addr); + const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr); + const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); + const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); + bool test_fail = word32 != rb_tx or word32 != rb_rx; + if (test_fail) { + UHD_MSG(status) << "fail" << std::endl; + throw uhd::runtime_error("CODEC loopback test failed."); + } + } + UHD_MSG(status) << "pass" << std::endl; + /* Zero out the idle data. */ + iface->poke32(codec_idle_addr, 0); + _codec_ctrl->data_port_loopback(false); + } + + + double get_auto_tick_rate( + const double lcm_rate, + size_t num_chans + ) { + UHD_ASSERT_THROW(num_chans >= 1 and num_chans <= _n_frontends); + const uhd::meta_range_t rate_range = _codec_ctrl->get_clock_rate_range(); + const double min_tick_rate = rate_range.start(); + const double max_tick_rate = rate_range.stop() / num_chans; + + // Check if the requested rate is within available limits: + if (uhd::math::fp_compare::fp_compare_delta<double>(lcm_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { + throw uhd::value_error(str( + boost::format("[ad936x_manager] Cannot get determine a tick rate if sampling rate exceeds maximum tick rate (%f > %f)") + % lcm_rate % max_tick_rate + )); + } + + // **** Choose the new rate **** + // Rules for choosing the tick rate: + // Choose a rate that is a power of 2 larger than the sampling rate, + // but at least 4. Cannot exceed the max tick rate, of course, but must + // be larger than the minimum tick rate. + // An equation that does all that is: + // + // f_auto = r * 2^floor(log2(f_max/r)) + // = lcm_rate * multiplier + // + // where r is the base rate and f_max is the maximum tick rate. The case + // where floor() yields 1 must be caught. + // We use shifts here instead of 2^x because exp2() is not available in all compilers, + // also this guarantees no rounding issues. The type cast to int32_t serves as floor(): + int32_t multiplier = (1 << int32_t(uhd::math::log2(max_tick_rate / lcm_rate))); + if (multiplier == 2 and lcm_rate >= min_tick_rate) { + // Don't bother (see above) + multiplier = 1; + } + const double new_rate = lcm_rate * multiplier; + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >= + uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <= + uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + + return new_rate; + } + + bool check_bandwidth(double rate, const std::string dir) + { + if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { + UHD_MSG(warning) + << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" + << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." + << std::endl; + return false; + } + return true; + } + + void populate_frontend_subtree(uhd::property_tree::sptr subtree, const std::string &key, uhd::direction_t dir) + { + subtree->create<std::string>("name").set("FE-"+key); + + // Sensors + subtree->create<sensor_value_t>("sensors/temp") + .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)) + ; + if (dir == RX_DIRECTION) { + subtree->create<sensor_value_t>("sensors/rssi") + .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)) + ; + } + + // Gains + BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) + { + subtree->create<meta_range_t>(uhd::fs_path("gains") / name / "range") + .set(ad9361_ctrl::get_gain_range(key)); + subtree->create<double>(uhd::fs_path("gains") / name / "value") + .set(ad936x_manager::DEFAULT_GAIN) + .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) + ; + } + + // FE Settings + subtree->create<std::string>("connection").set("IQ"); + subtree->create<bool>("enabled").set(true); + subtree->create<bool>("use_lo_offset").set(false); + + // Analog Bandwidths + subtree->create<double>("bandwidth/value") + .set(ad936x_manager::DEFAULT_BANDWIDTH) + .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) + ; + subtree->create<meta_range_t>("bandwidth/range") + .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)) + ; + + // LO Tuning + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)) + ; + subtree->create<double>("freq/value") + .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) + .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + ; + + // Frontend corrections + if(dir == RX_DIRECTION) + { + subtree->create<bool>("dc_offset/enable" ) + .set(ad936x_manager::DEFAULT_AUTO_DC_OFFSET) + .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)) + ; + subtree->create<bool>("iq_balance/enable" ) + .set(ad936x_manager::DEFAULT_AUTO_IQ_BALANCE) + .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1)) + ; + + // AGC setup + const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast"); + subtree->create<bool>("gain/agc/enable") + .set(DEFAULT_AGC_ENABLE) + .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) + ; + subtree->create<std::string>("gain/agc/mode/value") + .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front()) + ; + subtree->create< std::list<std::string> >("gain/agc/mode/options") + .set(mode_strings) + ; + } + + // Frontend filters + BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) { + subtree->create<filter_info_base::sptr>(uhd::fs_path("filters") / filter_name / "value" ) + .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name)) + .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1)); + } + } + + private: + //! Store a pointer to an actual AD936x control object + ad9361_ctrl::sptr _codec_ctrl; + + //! Do we have 1 or 2 frontends? + const size_t _n_frontends; + + //! List of valid RX frontend names (RX1, RX2) + std::vector<std::string> _rx_frontends; + //! List of valid TX frontend names (TX1, TX2) + std::vector<std::string> _tx_frontends; +}; /* class ad936x_manager_impl */ + +ad936x_manager::sptr ad936x_manager::make( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends +) { + return sptr( + new ad936x_manager_impl(codec_ctrl, n_frontends) + ); +} + diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp new file mode 100644 index 000000000..9b4a351c6 --- /dev/null +++ b/host/lib/usrp/common/ad936x_manager.hpp @@ -0,0 +1,132 @@ +// +// Copyright 2015 Ettus Research +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_MANAGER_HPP +#define INCLUDED_AD9361_MANAGER_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/types/direction.hpp> +#include <boost/shared_ptr.hpp> +#include "ad9361_ctrl.hpp" +#include <stdint.h> + +namespace uhd { namespace usrp { + +/*! AD936x Manager class + * + * This class performs higher (management) tasks on the AD936x. + * It requires a uhd::usrp::ad9361_ctrl object to do the actual + * register peeks/pokes etc. + */ +class ad936x_manager +{ +public: + typedef boost::shared_ptr<ad936x_manager> sptr; + + static const double DEFAULT_GAIN; + static const double DEFAULT_BANDWIDTH; + static const double DEFAULT_TICK_RATE; + static const double DEFAULT_FREQ; // Hz + static const uint32_t DEFAULT_DECIM; + static const uint32_t DEFAULT_INTERP; + static const bool DEFAULT_AUTO_DC_OFFSET; + static const bool DEFAULT_AUTO_IQ_BALANCE; + static const bool DEFAULT_AGC_ENABLE; + + /*! + * \param codec_ctrl The actual AD936x control object + * \param n_frontends Number of frontends (1 or 2) + */ + static sptr make( + const ad9361_ctrl::sptr &codec_ctrl, + const size_t n_frontends + ); + + virtual ~ad936x_manager(void) {}; + + /*! Put the AD936x into a default state. + * + * Sets gains, LOs, bandwidths, etc. according to the DEFAULT_* constants. + */ + virtual void init_codec(void) = 0; + + /*! Run a loopback self test. + * + * This will write data to the AD936x and read it back again. + * If this test fails, it generally means the interface is broken, + * so we assume it passes and throw otherwise. Running this requires + * a core that we can peek and poke the loopback values into. + * + * \param iface An interface to the associated radio control core + * \param iface The radio control core's address to write the loopback value + * \param iface The radio control core's readback address to read back the returned value + * + * \throws a uhd::runtime_error if the loopback value didn't match. + */ + virtual void loopback_self_test( + wb_iface::sptr iface, + wb_iface::wb_addr_type codec_idle_addr, + wb_iface::wb_addr_type codec_readback_addr + ) = 0; + + /*! Determine a tick rate that will work with a given sampling rate + * (assuming a DDC/DUC chain is also available elsewhere). + * + * Example: If we want to stream with a rate of 5 Msps, then the AD936x + * must run at an integer multiple of that. Although not strictly necessary, + * we always try and return a multiple of 2. Let's say we need those 5 Msps + * on two channels, then a good rate is 20 MHz, which is 4 times the sampling + * rate (thus we can use 2 halfbands elsewhere). + * If different rates are used on different channels, this can be particularly + * useful. The clock rate of the AD936x needs to be a multiple of the least + * common multiple of all the rates. Example: We want to transmit with 3 Msps + * and receive with 5 Msps. The LCM of this is 15 Msps, which is used as an + * argument for this function. A good rate is then 30 MHz, which is twice + * the LCM. + * + * \param lcm_rate Least Common Multiple of all the rates involved. + * \param num_chans The number of channels used for the stream. + * + * \returns a valid tick rate that can be used with the given rate + * \throws a uhd::value_error if \p lcm_rate exceeds the max tick rate + */ + virtual double get_auto_tick_rate( + const double lcm_rate, + size_t num_chans + ) = 0; + + /*! Check if a given sampling rate is within the available analog bandwidth. + * + * If not, outputs a warning message and returns false. + */ + virtual bool check_bandwidth(double rate, const std::string dir) = 0; + + /*! Populate the property tree for the device frontend + */ + virtual void populate_frontend_subtree( + uhd::property_tree::sptr subtree, + const std::string &key, + uhd::direction_t dir + ) = 0; + +}; /* class ad936x_manager */ + +}} /* namespace uhd::usrp */ + +#endif /* INCLUDED_AD9361_MANAGER_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 164437f40..e22834fd9 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,2014,2015 Ettus Research LLC // // 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 @@ -20,10 +20,34 @@ #include <uhd/config.hpp> #include <uhd/usrp/dboard_iface.hpp> +#include <boost/assign.hpp> #include <boost/cstdint.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/wb_iface.hpp> +#include <map> + +typedef enum { + GPIO_CTRL, + GPIO_DDR, + GPIO_OUT, + GPIO_ATR_0X, + GPIO_ATR_RX, + GPIO_ATR_TX, + GPIO_ATR_XX +} gpio_attr_t; + +typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; +static const gpio_attr_map_t gpio_attr_map = + boost::assign::map_list_of + (GPIO_CTRL, "CTRL") + (GPIO_DDR, "DDR") + (GPIO_OUT, "OUT") + (GPIO_ATR_0X, "ATR_0X") + (GPIO_ATR_RX, "ATR_RX") + (GPIO_ATR_TX, "ATR_TX") + (GPIO_ATR_XX, "ATR_XX") +; class gpio_core_200 : boost::noncopyable{ public: diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index 6a36e8fa1..b899085c0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -295,6 +295,7 @@ public: _iface->poke32(REG_RX_CTRL_FORMAT, format_word); } + private: wb_iface::sptr _iface; const size_t _dsp_base, _ctrl_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 8a131ffb4..18dabade0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -46,6 +46,9 @@ template <class T> T ceil_log2(T num){ using namespace uhd; +const double rx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double rx_dsp_core_3000::DEFAULT_RATE = 1e6; + rx_dsp_core_3000::~rx_dsp_core_3000(void){ /* NOP */ } @@ -263,6 +266,24 @@ public: this->update_scalar(); } + void populate_subtree(property_tree::sptr subtree) + { + subtree->create<meta_range_t>("rate/range") + .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this)) + ; + subtree->create<double>("rate/value") + .set(DEFAULT_RATE) + .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1)) + ; + subtree->create<double>("freq/value") + .set(DEFAULT_CORDIC_FREQ) + .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1)) + ; + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this)) + ; + } + private: wb_iface::sptr _iface; const size_t _dsp_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 89059e953..65801de1d 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -21,14 +21,18 @@ #include <uhd/config.hpp> #include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> #include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> #include <string> class rx_dsp_core_3000 : boost::noncopyable{ public: + static const double DEFAULT_CORDIC_FREQ; + static const double DEFAULT_RATE; + typedef boost::shared_ptr<rx_dsp_core_3000> sptr; virtual ~rx_dsp_core_3000(void) = 0; @@ -56,6 +60,8 @@ public: virtual double set_freq(const double freq) = 0; virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index b73896b57..7ac920553 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -17,6 +17,7 @@ #include "rx_frontend_core_200.hpp" #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> using namespace uhd; @@ -38,6 +39,10 @@ rx_frontend_core_200::~rx_frontend_core_200(void){ /* NOP */ } +const std::complex<double> rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const bool rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE = true; +const std::complex<double> rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); + class rx_frontend_core_200_impl : public rx_frontend_core_200{ public: rx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base): @@ -74,6 +79,22 @@ public: _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18)); } + void populate_subtree(uhd::property_tree::sptr subtree) + { + subtree->create<std::complex<double> >("dc_offset/value") + .set(DEFAULT_DC_OFFSET_VALUE) + .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1)) + ; + subtree->create<bool>("dc_offset/enable") + .set(DEFAULT_DC_OFFSET_ENABLE) + .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1)) + ; + subtree->create<std::complex<double> >("iq_balance/value") + .set(DEFAULT_IQ_BALANCE_VALUE) + .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1)) + ; + } + private: boost::int32_t _i_dc_off, _q_dc_off; wb_iface::sptr _iface; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index 9b18e2089..32ce77e00 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -19,14 +19,19 @@ #define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> class rx_frontend_core_200 : boost::noncopyable{ public: + static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; + static const bool DEFAULT_DC_OFFSET_ENABLE; + static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + typedef boost::shared_ptr<rx_frontend_core_200> sptr; virtual ~rx_frontend_core_200(void) = 0; @@ -41,6 +46,8 @@ public: virtual void set_iq_balance(const std::complex<double> &cor) = 0; + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + }; #endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index 736205402..93b70435f 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -37,6 +37,9 @@ template <class T> T ceil_log2(T num){ using namespace uhd; +const double tx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double tx_dsp_core_3000::DEFAULT_RATE = 1e6; + tx_dsp_core_3000::~tx_dsp_core_3000(void){ /* NOP */ } @@ -200,6 +203,24 @@ public: this->update_scalar(); } + void populate_subtree(property_tree::sptr subtree) + { + subtree->create<meta_range_t>("rate/range") + .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this)) + ; + subtree->create<double>("rate/value") + .set(DEFAULT_RATE) + .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1)) + ; + subtree->create<double>("freq/value") + .set(DEFAULT_CORDIC_FREQ) + .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1)) + ; + subtree->create<meta_range_t>("freq/range") + .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this)) + ; + } + private: wb_iface::sptr _iface; const size_t _dsp_base; diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp index a51cb2803..fbc43add6 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -21,12 +21,16 @@ #include <uhd/config.hpp> #include <uhd/stream.hpp> #include <uhd/types/ranges.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> class tx_dsp_core_3000 : boost::noncopyable{ public: + static const double DEFAULT_CORDIC_FREQ; + static const double DEFAULT_RATE; + typedef boost::shared_ptr<tx_dsp_core_3000> sptr; virtual ~tx_dsp_core_3000(void) = 0; @@ -51,6 +55,8 @@ public: virtual double set_freq(const double freq) = 0; virtual void setup(const uhd::stream_args_t &stream_args) = 0; + + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; }; #endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index 7000f46bd..0fa028571 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -20,6 +20,7 @@ #include <uhd/exception.hpp> #include <boost/assign/list_of.hpp> #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp> using namespace uhd; @@ -29,6 +30,9 @@ using namespace uhd; #define REG_TX_FE_PHASE_CORRECTION _base + 12 //18 bits #define REG_TX_FE_MUX _base + 16 //8 bits (std output = 0x10, reversed = 0x01) +const std::complex<double> tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const std::complex<double> tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); + static boost::uint32_t fs_to_bits(const double num, const size_t bits){ return boost::int32_t(boost::math::round(num * (1 << (bits-1)))); } @@ -71,6 +75,18 @@ public: _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18)); } + void populate_subtree(uhd::property_tree::sptr subtree) + { + subtree->create< std::complex<double> >("dc_offset/value") + .set(DEFAULT_DC_OFFSET_VALUE) + .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1)) + ; + subtree->create< std::complex<double> >("iq_balance/value") + .set(DEFAULT_IQ_BALANCE_VALUE) + .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1)) + ; + } + private: wb_iface::sptr _iface; const size_t _base; diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 0b89ea818..912256329 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -19,9 +19,10 @@ #define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> @@ -29,6 +30,9 @@ class tx_frontend_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<tx_frontend_core_200> sptr; + static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; + static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + virtual ~tx_frontend_core_200(void) = 0; static sptr make(uhd::wb_iface::sptr iface, const size_t base); @@ -39,6 +43,8 @@ public: virtual void set_iq_balance(const std::complex<double> &cor) = 0; + virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + }; #endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index 8336117b8..daf9a8dfd 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -38,7 +38,7 @@ sbx_xcvr::cbx::~cbx(void){ /* NOP */ } -void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s) +void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) { BOOST_FOREACH(boost::uint32_t reg, regs) { diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index a08d22537..4800bbd83 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2014 Ettus Research LLC +// Copyright 2011-2015 Ettus Research LLC // // 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 @@ -225,7 +225,7 @@ protected: /*! This is the registered instance of the wrapper class, sbx_base. */ sbx_xcvr *self_base; private: - void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s); + void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s); max287x_iface::sptr _txlo; max287x_iface::sptr _rxlo; }; diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp index f2bee47a9..3b56ae19a 100644 --- a/host/lib/usrp/dboard_eeprom.cpp +++ b/host/lib/usrp/dboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 Ettus Research LLC // // 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 @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/types/byte_vector.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/exception.hpp> #include <uhd/utils/log.hpp> @@ -27,30 +28,6 @@ using namespace uhd; using namespace uhd::usrp; -/*********************************************************************** - * Utility functions - **********************************************************************/ - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //////////////////////////////////////////////////////////////////////// // format of daughterboard EEPROM // 00: 0xDB code for ``I'm a daughterboard'' diff --git a/host/lib/usrp/dboard_eeprom_c.cpp b/host/lib/usrp/dboard_eeprom_c.cpp new file mode 100644 index 000000000..e3ef4933f --- /dev/null +++ b/host/lib/usrp/dboard_eeprom_c.cpp @@ -0,0 +1,109 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/dboard_eeprom.h> +#include <uhd/error.h> + +#include <boost/lexical_cast.hpp> + +#include <string.h> + +uhd_error uhd_dboard_eeprom_make( + uhd_dboard_eeprom_handle* h +){ + UHD_SAFE_C( + *h = new uhd_dboard_eeprom_t; + ) +} + +uhd_error uhd_dboard_eeprom_free( + uhd_dboard_eeprom_handle* h +){ + UHD_SAFE_C( + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_dboard_eeprom_get_id( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string dboard_id_cpp = h->dboard_eeprom_cpp.id.to_string(); + strncpy(id_out, dboard_id_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_dboard_eeprom_set_id( + uhd_dboard_eeprom_handle h, + const char* id +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.id = uhd::usrp::dboard_id_t::from_string(id); + ) +} + +uhd_error uhd_dboard_eeprom_get_serial( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string dboard_serial_cpp = h->dboard_eeprom_cpp.serial; + strncpy(id_out, dboard_serial_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_dboard_eeprom_set_serial( + uhd_dboard_eeprom_handle h, + const char* serial +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.serial = serial; + ) +} + +uhd_error uhd_dboard_eeprom_get_revision( + uhd_dboard_eeprom_handle h, + int* revision_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *revision_out = boost::lexical_cast<int>(h->dboard_eeprom_cpp.revision); + ) +} + +uhd_error uhd_dboard_eeprom_set_revision( + uhd_dboard_eeprom_handle h, + int revision +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->dboard_eeprom_cpp.revision = boost::lexical_cast<std::string>(revision); + ) +} + +uhd_error uhd_dboard_eeprom_last_error( + uhd_dboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt index ac9d8c655..2a1e14eab 100644 --- a/host/lib/usrp/e100/CMakeLists.txt +++ b/host/lib/usrp/e100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the USRP-E100 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF) +LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF OFF) IF(ENABLE_E100) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index ac419e0e0..6d3c08534 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012,2014 Ettus Research LLC +// Copyright 2010-2012,2014-2015 Ettus Research LLC // // 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 @@ -28,6 +28,7 @@ #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> +#include <iostream> #include <ctime> using namespace uhd; @@ -45,7 +46,7 @@ namespace fs = boost::filesystem; /*********************************************************************** * Discovery **********************************************************************/ -static device_addrs_t e100_find(const device_addr_t &hint){ +device_addrs_t e100_find(const device_addr_t &hint){ device_addrs_t e100_addrs; //return an empty list of addresses when type is set to non-usrp-e @@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost ("E110", "usrp_e110_fpga.bin") ; -/*********************************************************************** - * Structors - **********************************************************************/ -e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ - _tree = property_tree::make(); - _type = device::USRP; - _ignore_cal_file = device_addr.has_key("ignore-cal-file"); - +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){ //read the eeprom so we can determine the hardware - _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); + uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); + const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ ) % model)); //extract the fpga path and compute hash - const std::string default_fpga_file_name = model_to_fpga_file_name[model]; + return model_to_fpga_file_name[model]; +} + +/*********************************************************************** + * Structors + **********************************************************************/ +e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ + _tree = property_tree::make(); + _type = device::USRP; + _ignore_cal_file = device_addr.has_key("ignore-cal-file"); + + _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); + const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); + const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr); + const std::string model = device_addr["model"]; std::string e100_fpga_image; try{ e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 4efc21427..d00668224 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -29,6 +29,7 @@ #include "recv_packet_demuxer.hpp" #include <uhd/device.hpp> #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/usrp/mboard_eeprom.hpp> @@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface( e100_codec_ctrl::sptr codec ); +uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint); +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr); + /*! * USRP-E100 implementation guts: * The implementation details are encapsulated here. diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp index c9d77f560..9abde32f7 100644 --- a/host/lib/usrp/e100/fpga_downloader.cpp +++ b/host/lib/usrp/e100/fpga_downloader.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2010-2011,2014-2015 Ettus Research LLC // // 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 @@ -17,8 +17,16 @@ #include <uhd/config.hpp> #ifdef UHD_DLL_EXPORTS +#include <boost/filesystem.hpp> +#include <boost/format.hpp> #include <uhd/exception.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e100_impl.hpp" #else //special case when this file is externally included #include <stdexcept> #include <iostream> @@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){ } +#ifdef UHD_DLL_EXPORTS +namespace fs = boost::filesystem; + +static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){ + // Make sure this is an E1x0 + uhd::device_addrs_t devs = e100_find(uhd::device_addr_t()); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + std::string fpga_filename; + if(image_loader_args.fpga_path == ""){ + fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0])); + } + else{ + if(not fs::exists(image_loader_args.fpga_path)){ + throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") + % image_loader_args.fpga_path)); + } + else fpga_filename = image_loader_args.fpga_path; + } + + e100_load_fpga(fpga_filename); + return true; +} + +UHD_STATIC_BLOCK(register_e100_image_loader){ + std::string recovery_instructions = "The default FPGA image will be loaded the next time " + "UHD uses this device."; + + uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions); +} +#endif /* UHD_DLL_EXPORTS */ diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt index 9ee9b5521..9c8aa29b9 100644 --- a/host/lib/usrp/e300/CMakeLists.txt +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-2015 Ettus Research LLC # # 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 @@ -24,7 +24,7 @@ ######################################################################## find_package(UDev) -LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_E300) LIST(APPEND E300_SOURCES @@ -39,17 +39,23 @@ IF(ENABLE_E300) ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp ) LIBUHD_APPEND_SOURCES(${E300_SOURCES}) - IF(UDEV_FOUND) + IF(UDEV_FOUND AND NOT E300_FORCE_NETWORK) INCLUDE_DIRECTORIES(${UDEV_INCLUDE_DIR}) LIBUHD_APPEND_LIBS(${UDEV_LIBS}) SET_SOURCE_FILES_PROPERTIES( ${E300_SOURCES} PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1" ) - ENDIF(UDEV_FOUND) + ENDIF(UDEV_FOUND AND NOT E300_FORCE_NETWORK) + + IF(ENABLE_GPSD) + SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp + PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1" + ) + ENDIF(ENABLE_GPSD) ENDIF(ENABLE_E300) diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp deleted file mode 100644 index cdf18f7f7..000000000 --- a/host/lib/usrp/e300/e300_async_serial.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -async_serial::async_serial() - : _io(), - _port(_io), - _background_thread(), - _open(false), - _error(false) -{ -} - -async_serial::async_serial( - const std::string &node, - const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity, - boost::asio::serial_port_base::character_size opt_csize, - boost::asio::serial_port_base::flow_control opt_flow, - boost::asio::serial_port_base::stop_bits opt_stop) - : _io(), - _port(_io), - _background_thread(), - _open(false), - _error(false) -{ - open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop); -} - -void async_serial::open( - const std::string &node, - const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity, - boost::asio::serial_port_base::character_size opt_csize, - boost::asio::serial_port_base::flow_control opt_flow, - boost::asio::serial_port_base::stop_bits opt_stop) -{ - if(is_open()) - close(); - - _set_error_status(true); - _port.open(node); - _port.set_option( - boost::asio::serial_port_base::baud_rate(baud_rate)); - _port.set_option(opt_parity); - _port.set_option(opt_csize); - _port.set_option(opt_flow); - _port.set_option(opt_stop); - - _io.post(boost::bind(&async_serial::_do_read, this)); - - boost::thread t(boost::bind(&boost::asio::io_service::run, &_io)); - _background_thread.swap(t); - _set_error_status(false); - _open=true; -} - -bool async_serial::is_open() const -{ - return _open; -} - -bool async_serial::error_status() const -{ - boost::lock_guard<boost::mutex> l(_error_mutex); - return _error; -} - -void async_serial::close() -{ - if(!is_open()) - return; - - _open=false; - _io.post(boost::bind(&async_serial::_do_close, this)); - _background_thread.join(); - _io.reset(); - if(error_status()) - throw(boost::system::system_error(boost::system::error_code(), - "Error while closing the device")); -} - -void async_serial::write(const char *data, size_t size) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert(_write_queue.end(), data, data+size); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write(const std::vector<char> &data) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert( - _write_queue.end(), - data.begin(), - data.end()); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write_string(const std::string &s) -{ - { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_queue.insert( - _write_queue.end(), - s.begin(), - s.end()); - } - _io.post(boost::bind(&async_serial::_do_write, this)); -} - -async_serial::~async_serial() -{ - if(is_open()) { - try { - close(); - } catch(...) { - //Don't throw from a destructor - } - } -} - -void async_serial::_do_read() -{ - _port.async_read_some(boost::asio::buffer( - _read_buffer,READ_BUFFER_SIZE), - boost::bind(&async_serial::_read_end, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); -} - -void async_serial::_read_end( - const boost::system::error_code& error, - size_t bytes_transferred) -{ - if(error) { - if(is_open()) { - _do_close(); - _set_error_status(true); - } - } else { - if(_callback) - _callback( - _read_buffer, - bytes_transferred); - _do_read(); - } -} - -void async_serial::_do_write() -{ - // if a write operation is already in progress, do nothing - if(_write_buffer == 0) { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - _write_buffer_size=_write_queue.size(); - _write_buffer.reset(new char[_write_queue.size()]); - std::copy(_write_queue.begin(),_write_queue.end(), - _write_buffer.get()); - _write_queue.clear(); - async_write( - _port, boost::asio::buffer(_write_buffer.get(), - _write_buffer_size), - boost::bind( - &async_serial::_write_end, - this, - boost::asio::placeholders::error)); - } -} - -void async_serial::_write_end(const boost::system::error_code& error) -{ - if(!error) { - boost::lock_guard<boost::mutex> l(_write_queue_mutex); - if(_write_queue.empty()) { - _write_buffer.reset(); - _write_buffer_size=0; - return; - } - _write_buffer_size = _write_queue.size(); - _write_buffer.reset(new char[_write_queue.size()]); - std::copy(_write_queue.begin(),_write_queue.end(), - _write_buffer.get()); - _write_queue.clear(); - async_write( - _port, - boost::asio::buffer(_write_buffer.get(), - _write_buffer_size), - boost::bind( - &async_serial::_write_end, - this, - boost::asio::placeholders::error)); - } else { - _set_error_status(true); - _do_close(); - } -} - -void async_serial::_do_close() -{ - boost::system::error_code ec; - _port.cancel(ec); - if(ec) - _set_error_status(true); - _port.close(ec); - if(ec) - _set_error_status(true); -} - -void async_serial::_set_error_status(const bool e) -{ - boost::lock_guard<boost::mutex> l(_error_mutex); - _error = e; -} - - -void async_serial::set_read_callback( - const boost::function<void (const char*, size_t)> &callback) -{ - _callback = callback; -} - - -}}} // namespace diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp deleted file mode 100644 index fafc7de3d..000000000 --- a/host/lib/usrp/e300/e300_async_serial.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// -// 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 -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_ASYNC_SERIAL_HPP -#define INCLUDED_ASYNC_SERIAL_HPP - -#include <boost/asio.hpp> -#include <boost/bind.hpp> -#include <boost/thread.hpp> -#include <boost/utility.hpp> -#include <boost/function.hpp> -#include <boost/shared_array.hpp> - -namespace uhd { namespace usrp { namespace gps { - -class async_serial : private boost::noncopyable -{ -public: - async_serial(); - ~async_serial(); - - async_serial(const std::string &node, const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity= - boost::asio::serial_port_base::parity( - boost::asio::serial_port_base::parity::none), - boost::asio::serial_port_base::character_size opt_csize= - boost::asio::serial_port_base::character_size(8), - boost::asio::serial_port_base::flow_control opt_flow= - boost::asio::serial_port_base::flow_control( - boost::asio::serial_port_base::flow_control::none), - boost::asio::serial_port_base::stop_bits opt_stop= - boost::asio::serial_port_base::stop_bits( - boost::asio::serial_port_base::stop_bits::one)); - - void open(const std::string& node, const size_t baud_rate, - boost::asio::serial_port_base::parity opt_parity= - boost::asio::serial_port_base::parity( - boost::asio::serial_port_base::parity::none), - boost::asio::serial_port_base::character_size opt_csize= - boost::asio::serial_port_base::character_size(8), - boost::asio::serial_port_base::flow_control opt_flow= - boost::asio::serial_port_base::flow_control( - boost::asio::serial_port_base::flow_control::none), - boost::asio::serial_port_base::stop_bits opt_stop= - boost::asio::serial_port_base::stop_bits( - boost::asio::serial_port_base::stop_bits::one)); - - bool is_open(void) const; - - bool error_status(void) const; - - void close(void); - - void write(const char *data, const size_t size); - void write(const std::vector<char> &data); - - void write_string(const std::string &s); - - static const size_t READ_BUFFER_SIZE=512; - - void set_read_callback( - const boost::function<void (const char*, size_t)>& callback); - - void clear_callback(); - -private: // methods - void _do_read(); - - void _read_end( - const boost::system::error_code &error, - size_t bytes_transferred); - - void _do_write(); - - void _write_end(const boost::system::error_code &error); - - void _do_close(); - - void _set_error_status(const bool e); -private: // members - boost::asio::io_service _io; - boost::asio::serial_port _port; - boost::thread _background_thread; - bool _open; - bool _error; - mutable boost::mutex _error_mutex; - - std::vector<char> _write_queue; - boost::shared_array<char> _write_buffer; - size_t _write_buffer_size; - boost::mutex _write_queue_mutex; - char _read_buffer[READ_BUFFER_SIZE]; - - boost::function<void (const char*, size_t)> _callback; -}; - -}}} // namespace - -#endif //INCLUDED_ASYNC_SERIAL_HPP diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp index db5b37055..216713bc6 100644 --- a/host/lib/usrp/e300/e300_common.cpp +++ b/host/lib/usrp/e300/e300_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // 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 @@ -14,8 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/image_loader.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e300_impl.hpp" #include "e300_fifo_config.hpp" #include "e300_fifo_config.hpp" @@ -23,7 +27,9 @@ #include <boost/filesystem.hpp> #include <fstream> +#include <string> +#ifdef E300_NATIVE namespace uhd { namespace usrp { namespace e300 { namespace common { @@ -54,6 +60,49 @@ void load_fpga_image(const std::string &path) UHD_MSG(status) << " done" << std::endl; } +static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) { + // Make sure this is an E3x0 and we don't want to use anything connected + uhd::device_addrs_t devs = e300_find(image_loader_args.args); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + std::string fpga_filename, idle_image; // idle_image never used, just needed for function + if(image_loader_args.fpga_path == "") { + get_e3x0_fpga_images(devs[0], fpga_filename, idle_image); + } + else { + if(not boost::filesystem::exists(image_loader_args.fpga_path)) { + throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") + % image_loader_args.fpga_path)); + } + else fpga_filename = image_loader_args.fpga_path; + } + + load_fpga_image(fpga_filename); + return true; +} + +UHD_STATIC_BLOCK(register_e300_image_loader) { + std::string recovery_instructions = "The default FPGA image will be loaded the next " + "time UHD uses this device."; + + image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions); +} + +} + +}}} + +#else +namespace uhd { namespace usrp { namespace e300 { + +namespace common { + +void load_fpga_image(const std::string&) +{ + throw uhd::assertion_error("load_fpga_image() !E300_NATIVE"); +} + } }}} +#endif diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp index d409062c5..267897e03 100644 --- a/host/lib/usrp/e300/e300_defaults.hpp +++ b/host/lib/usrp/e300/e300_defaults.hpp @@ -23,18 +23,13 @@ namespace uhd { namespace usrp { namespace e300 { static const double DEFAULT_TICK_RATE = 32e6; -static const double MAX_TICK_RATE = 50e6; -static const double MIN_TICK_RATE = 1e6; +static const double MIN_TICK_RATE = 10e6; static const double DEFAULT_TX_SAMP_RATE = 1.0e6; static const double DEFAULT_RX_SAMP_RATE = 1.0e6; static const double DEFAULT_DDC_FREQ = 0.0; static const double DEFAULT_DUC_FREQ = 0.0; -static const double DEFAULT_FE_GAIN = 0.0; -static const double DEFAULT_FE_FREQ = 1.0e9; -static const double DEFAULT_FE_BW = 56e6; - static const std::string DEFAULT_TIME_SRC = "internal"; static const std::string DEFAULT_CLOCK_SRC = "internal"; @@ -73,7 +68,7 @@ public: digital_interface_delays_t get_digital_interface_timing() { digital_interface_delays_t delays; delays.rx_clk_delay = 0; - delays.rx_data_delay = 0xF; + delays.rx_data_delay = 0x8; delays.tx_clk_delay = 0; delays.tx_data_delay = 0xF; return delays; diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index eea4d7f63..f514ec9e6 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga { static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 8; +static const boost::uint32_t COMPAT_MAJOR = 10; static const boost::uint32_t COMPAT_MINOR = 0; }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index a08168eab..6d66e83c0 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // 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 @@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs) return if_addrs.inet == asio::ip::address_v4::loopback().to_string(); } -static device_addrs_t e300_find(const device_addr_t &multi_dev_hint) +device_addrs_t e300_find(const device_addr_t &multi_dev_hint) { // handle multi device discovery device_addrs_t hints = separate_device_addr(multi_dev_hint); @@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr) return device::sptr(new e300_impl(device_addr)); } +// Common code used by e300_impl and e300_image_loader +void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr, + std::string &fpga_image, + std::string &idle_image){ + const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( + device_addr["product"]); + + //extract the FPGA path for the e300 + switch(e300_eeprom_manager::get_mb_type(pid)) { + case e300_eeprom_manager::USRP_E310_MB: + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E310_FPGA_FILE_NAME)); + idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); + break; + case e300_eeprom_manager::USRP_E300_MB: + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E300_FPGA_FILE_NAME)); + idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); + break; + case e300_eeprom_manager::UNKNOWN: + default: + UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." + << std::endl; + fpga_image = device_addr.cast<std::string>("fpga", + find_image_path(E300_FPGA_FILE_NAME)); + idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); + break; + } +} + /*********************************************************************** * Structors **********************************************************************/ @@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) if (_xport_path == AXI) { _do_not_reload = device_addr.has_key("no_reload_fpga"); if (not _do_not_reload) { - // Load FPGA image if provided via args - const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( - device_addr["product"]); - std::string fpga_image; - - //extract the FPGA path for the e300 - switch(e300_eeprom_manager::get_mb_type(pid)) { - case e300_eeprom_manager::USRP_E310_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E310_FPGA_FILE_NAME)); - _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::USRP_E300_MB: - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); - break; - case e300_eeprom_manager::UNKNOWN: - default: - UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." - << std::endl; - fpga_image = device_addr.cast<std::string>("fpga", - find_image_path(E300_FPGA_FILE_NAME)); - _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); - break; - } + get_e3x0_fpga_images(device_addr, + fpga_image, + _idle_image); common::load_fpga_image(fpga_image); } } @@ -387,18 +394,35 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) // This is horrible ... why do I have to sleep here? boost::this_thread::sleep(boost::posix_time::milliseconds(100)); _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); + _sensor_manager = e300_sensor_manager::make_local(_global_regs); } + _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS); - UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; - if (_xport_path == AXI) { - try { - _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600); - } catch (std::exception &e) { - UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl; +#ifdef E300_GPSD + UHD_MSG(status) << "Detecting internal GPSDO " << std::flush; + try { + if (_xport_path == AXI) + _gps = gpsd_iface::make("localhost", 2947); + else + _gps = gpsd_iface::make(device_addr["addr"], 2947); + } catch (std::exception &e) { + UHD_MSG(error) << "An error occured making GPSDd interface: " << e.what() << std::endl; + } + + if (_gps) { + for (size_t i = 0; i < _GPS_TIMEOUT; i++) + { + boost::this_thread::sleep(boost::posix_time::seconds(1)); + if (!_gps->gps_detected()) + std::cout << "." << std::flush; + else { + std::cout << ".... " << std::flush; + break; + } } - _sensor_manager = e300_sensor_manager::make_local(_gps, _global_regs); + UHD_MSG(status) << (_gps->gps_detected() ? "found" : "not found") << std::endl; } - UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found") << std::endl; +#endif // Verify we can talk to the e300 core control registers ... UHD_MSG(status) << "Initializing core control..." << std::endl; @@ -443,6 +467,15 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name)); } +#ifdef E300_GPSD + if (_gps) { + BOOST_FOREACH(const std::string &name, _gps->get_sensors()) + { + _tree->create<sensor_value_t>(mb_path / "sensors" / name) + .publish(boost::bind(&gpsd_iface::get_sensor, _gps, name)); + } + } +#endif //////////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -471,28 +504,23 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++) this->_setup_radio(instance); - _codec_ctrl->data_port_loopback(true); - // Radio 0 loopback through AD9361 - this->_codec_loopback_self_test(_radio_perifs[0].ctrl); + _codec_mgr->loopback_self_test(_radio_perifs[0].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); // Radio 1 loopback through AD9361 - this->_codec_loopback_self_test(_radio_perifs[1].ctrl); - - _codec_ctrl->data_port_loopback(false); + _codec_mgr->loopback_self_test(_radio_perifs[1].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); //////////////////////////////////////////////////////////////////// // internal gpios //////////////////////////////////////////////////////////////////// - gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, gpio_attrs) + gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr) - .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1)) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) + .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1)) .set(0); } _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") - .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK")); + .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio)); //////////////////////////////////////////////////////////////////// @@ -510,7 +538,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) _tree->create<std::string>(mb_path / "time_source" / "value") .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1)) .set(e300::DEFAULT_TIME_SRC); +#ifdef E300_GPSD static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); +#else + static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external"); +#endif _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources); //setup reference source props _tree->create<std::string>(mb_path / "clock_source" / "value") @@ -575,7 +607,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) // init the clock rate to something reasonable _tree->access<double>(mb_path / "tick_rate").set( - device_addr.cast<double>("master_clock_rate", e300::DEFAULT_TICK_RATE)); + device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE)); // subdev spec contains full width of selections subdev_spec_t rx_spec, tx_spec; @@ -589,41 +621,37 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr) } _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); - - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_sensor_manager->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - - // wait for time to be actually set - boost::this_thread::sleep(boost::posix_time::seconds(1)); } -boost::uint8_t e300_impl::_get_internal_gpio( - gpio_core_200::sptr gpio, - const std::string &) +boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } void e300_impl::_set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - else if (attr == "DDR") + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - else if (attr == "OUT") + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - else if (attr == "ATR_0X") + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - else if (attr == "ATR_RX") + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - else if (attr == "ATR_TX") + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - else if (attr == "ATR_XX") + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: + UHD_THROW_INVALID_CODE_PATH(); + } } uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx) @@ -665,6 +693,17 @@ void e300_impl::_enforce_tick_rate_limits( % direction )); } + // Minimum rate restriction due to MMCM used in capture interface to AD9361. + // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz. + const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2); + if (tick_rate - min_tick_rate < 0.0) + { + throw uhd::value_error(boost::str( + boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)") + % (tick_rate/1e6) + % (min_tick_rate/1e6) + )); + } } } @@ -690,8 +729,8 @@ void e300_impl::_register_loopback_self_test(wb_iface::sptr iface) for (size_t i = 0; i < 100; i++) { boost::hash_combine(hash, i); - iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); - test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -721,30 +760,6 @@ std::string e300_impl::_get_version_hash(void) % ((git_hash & 0xF000000) ? "-dirty" : "")); } -void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface) -{ - bool test_fail = false; - UHD_ASSERT_THROW(bool(iface)); - UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; - size_t hash = size_t(time(NULL)); - for (size_t i = 0; i < 100; i++) - { - boost::hash_combine(hash, i); - const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0; - iface->poke32(TOREG(SR_CODEC_IDLE), word32); - iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate - const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK); - const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); - const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); - test_fail = word32 != rb_tx or word32 != rb_rx; - if (test_fail) break; //exit loop on any failure - } - UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; - - /* Zero out the idle data. */ - iface->poke32(TOREG(SR_CODEC_IDLE), 0); -} - boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config) { const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; @@ -799,8 +814,10 @@ void e300_impl::_update_time_source(const std::string &source) UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl; if (source == "none" or source == "internal") { _misc.pps_sel = global_regs::PPS_INT; +#ifdef E300_GPSD } else if (source == "gpsdo") { _misc.pps_sel = global_regs::PPS_GPS; +#endif } else if (source == "external") { _misc.pps_sel = global_regs::PPS_EXT; } else { @@ -928,6 +945,7 @@ void e300_impl::_setup_radio(const size_t dspno) { radio_perifs_t &perif = _radio_perifs[dspno]; const fs_path mb_path = "/mboards/0"; + std::string slot_name = (dspno == 0) ? "A" : "B"; //////////////////////////////////////////////////////////////////// // crossbar config for ctrl xports @@ -956,137 +974,99 @@ void e300_impl::_setup_radio(const size_t dspno) ctrl_sid, dspno ? "1" : "0"); this->_register_loopback_self_test(perif.ctrl); - perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO)); + + //////////////////////////////////////////////////////////////////// + // Set up peripherals + //////////////////////////////////////////////////////////////////// + perif.atr = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::GPIO)); + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT)); + perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); + perif.rx_fe->set_iq_balance(rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT)); + perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + perif.ddc->set_freq(e300::DEFAULT_DDC_FREQ); + perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + perif.duc->set_freq(e300::DEFAULT_DUC_FREQ); + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = radio::RB64_TIME_NOW; + time64_rb_bases.rb_pps = radio::RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases); //////////////////////////////////////////////////////////////////// // front end corrections //////////////////////////////////////////////////////////////////// - std::string slot_name = (dspno == 0) ? "A" : "B"; - perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); - const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; - _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") - .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) - .set(true); - _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") - .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - - perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); - const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; - _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") - .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); + perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); + perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // connect rx dsp control objects //////////////////////////////////////////////////////////////////// - perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); - perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); - perif.ddc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % dspno); - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1)) - .set(e300::DEFAULT_RX_SAMP_RATE); - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(e300::DEFAULT_DDC_FREQ); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + ; _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// // create tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % dspno); - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1)) - .set(e300::DEFAULT_TX_SAMP_RATE); - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(e300::DEFAULT_DUC_FREQ); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + ; //////////////////////////////////////////////////////////////////// // create RF frontend interfacing //////////////////////////////////////////////////////////////////// - static const std::vector<std::string> data_directions = boost::assign::list_of("rx")("tx"); - BOOST_FOREACH(const std::string& direction, data_directions) - { - const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2")); + static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); + BOOST_FOREACH(direction_t dir, dirs) { + const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx"; + const std::string key = boost::to_upper_copy(x) + std::string(((dspno == FE0)? "1" : "2")); const fs_path rf_fe_path - = mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B"); + = mb_path / "dboards" / "A" / (x + "_frontends") / ((dspno == 0) ? "A" : "B"); - _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key); - _tree->create<int>(rf_fe_path / "sensors"); //empty TODO - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") - .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx")); - BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key)) - { - _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range") - .set(ad9361_ctrl::get_gain_range(key)); + // This will connect all the AD936x-specific items + _codec_mgr->populate_frontend_subtree( + _tree->subtree(rf_fe_path), key, dir + ); - _tree->create<double>(rf_fe_path / "gains" / name / "value") - .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_GAIN); - } - _tree->create<std::string>(rf_fe_path / "connection").set("IQ"); - _tree->create<bool>(rf_fe_path / "enabled").set(true); - _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false); - _tree->create<double>(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_BW); - _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create<double>(rf_fe_path / "freq" / "value") - .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) - .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) + // This will connect all the e300_impl-specific items + _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") + .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION)) + ; + _tree->access<double>(rf_fe_path / "freq" / "value") .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1)) - .set(e300::DEFAULT_FE_FREQ); - _tree->create<meta_range_t>(rf_fe_path / "freq" / "range") - .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); + ; - //setup RX related stuff - if (key[0] == 'R') { + // Antenna Setup + if (dir == RX_DIRECTION) { static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1)) .set("RX2"); - _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi") - .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); } - if (key[0] == 'T') { + else if (dir == TX_DIRECTION) { static const std::vector<std::string> ants(1, "TX/RX"); _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants); _tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX"); diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 7f83c16ed..8aff51466 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // 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 @@ -20,6 +20,7 @@ #include <uhd/device.hpp> #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> @@ -28,6 +29,7 @@ #include <uhd/types/sensors.hpp> #include <boost/weak_ptr.hpp> #include <boost/thread/mutex.hpp> +#include <string> #include "e300_fifo_config.hpp" #include "radio_ctrl_core_3000.hpp" #include "rx_frontend_core_200.hpp" @@ -38,13 +40,18 @@ #include "rx_dsp_core_3000.hpp" #include "tx_dsp_core_3000.hpp" #include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" #include "gpio_core_200.hpp" #include "e300_global_regs.hpp" #include "e300_i2c.hpp" #include "e300_eeprom_manager.hpp" #include "e300_sensor_manager.hpp" -#include "e300_ublox_control.hpp" + +/* if we don't compile with gpsd support, don't bother */ +#ifdef E300_GPSD +#include "gpsd_iface.hpp" +#endif namespace uhd { namespace usrp { namespace e300 { @@ -98,6 +105,10 @@ static const size_t E300_R1_CTRL_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_C static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX; static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX; +uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint); +void get_e3x0_fpga_images(const uhd::device_addr_t &device_args, + std::string &fpga_image, + std::string &idle_image); /*! * USRP-E300 implementation guts: @@ -267,13 +278,11 @@ private: // methods uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); // internal gpios - boost::uint8_t _get_internal_gpio( - gpio_core_200::sptr, - const std::string &); + boost::uint8_t _get_internal_gpio(gpio_core_200::sptr); void _set_internal_gpio( gpio_core_200::sptr gpio, - const std::string &attr, + const gpio_attr_t attr, const boost::uint32_t value); private: // members @@ -284,6 +293,7 @@ private: // members radio_perifs_t _radio_perifs[2]; double _tick_rate; ad9361_ctrl::sptr _codec_ctrl; + ad936x_manager::sptr _codec_mgr; fe_control_settings_t _settings; global_regs::sptr _global_regs; e300_sensor_manager::sptr _sensor_manager; @@ -293,7 +303,10 @@ private: // members std::string _idle_image; bool _do_not_reload; gpio_t _misc; - gps::ublox::ubx::control::sptr _gps; +#ifdef E300_GPSD + gpsd_iface::sptr _gps; + static const size_t _GPS_TIMEOUT = 5; +#endif }; }}} // namespace diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp index dadfb71e9..29d250c8f 100644 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -91,21 +91,13 @@ void e300_impl::_update_tick_rate(const double rate) } } -#define CHECK_BANDWIDTH(dir) \ - if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ - UHD_MSG(warning) \ - << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ - << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ - << std::endl; \ - } - void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate) { boost::shared_ptr<sph::recv_packet_streamer> my_streamer = boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock()); if (my_streamer) my_streamer->set_samp_rate(rate); - CHECK_BANDWIDTH("Rx"); + _codec_mgr->check_bandwidth(rate, "Rx"); } void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) @@ -114,7 +106,7 @@ void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock()); if (my_streamer) my_streamer->set_samp_rate(rate); - CHECK_BANDWIDTH("Tx"); + _codec_mgr->check_bandwidth(rate, "Tx"); } /*********************************************************************** diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp index 408f9e62d..96387d6f7 100644 --- a/host/lib/usrp/e300/e300_network.cpp +++ b/host/lib/usrp/e300/e300_network.cpp @@ -230,6 +230,27 @@ static void e300_codec_ctrl_tunnel( case codec_xact_t::ACTION_GET_RSSI: out->rssi = _codec_ctrl->get_rssi(which_str).to_real(); break; + case codec_xact_t::ACTION_GET_TEMPERATURE: + out->temp = _codec_ctrl->get_temperature().to_real(); + break; + case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO: + _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1); + break; + case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO: + _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1); + case codec_xact_t::ACTION_SET_AGC: + _codec_ctrl->set_agc(which_str, in->use_agc == 1); + break; + case codec_xact_t::ACTION_SET_AGC_MODE: + if(in->agc_mode == 0) { + _codec_ctrl->set_agc_mode(which_str, "slow"); + } else if (in->agc_mode == 1) { + _codec_ctrl->set_agc_mode(which_str, "fast"); + } + break; + case codec_xact_t::ACTION_SET_BW: + out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw); + break; default: UHD_MSG(status) << "Got unknown request?!" << std::endl; //Zero out actions to fail this request on client @@ -328,19 +349,9 @@ static void e300_sensor_tunnel( // TODO: This is ugly ... use proper serialization in->value = uhd::htonx<boost::uint32_t>( e300_sensor_manager::pack_float_in_uint32_t(temp.to_real())); - } else if (uhd::ntohx(in->which) == GPS_FOUND) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_found() ? 1 : 0); - - } else if (uhd::ntohx(in->which) == GPS_LOCK) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_lock().to_bool() ? 1 : 0); } else if (uhd::ntohx(in->which) == REF_LOCK) { in->value = uhd::htonx<boost::uint32_t>( sensor_manager->get_ref_lock().to_bool() ? 1 : 0); - } else if (uhd::ntohx(in->which) == GPS_TIME) { - in->value = uhd::htonx<boost::uint32_t>( - sensor_manager->get_gps_time().to_int()); } else UHD_MSG(status) << "Got unknown request?!" << std::endl; @@ -627,8 +638,7 @@ network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr) _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1); // This is horrible ... why do I have to sleep here? boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - _sensor_manager = e300_sensor_manager::make_local( - gps::ublox::ubx::control::make("/dev/ttyPS1", 9600), _global_regs); + _sensor_manager = e300_sensor_manager::make_local(_global_regs); } }}} // namespace diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp index 5736ebfd4..846c759a4 100644 --- a/host/lib/usrp/e300/e300_regs.hpp +++ b/host/lib/usrp/e300/e300_regs.hpp @@ -18,36 +18,48 @@ #ifndef INCLUDED_E300_REGS_HPP #define INCLUDED_E300_REGS_HPP -#include <boost/cstdint.hpp> +#include <stdint.h> +#include <uhd/config.hpp> -#define TOREG(x) ((x)*4) +namespace uhd { namespace usrp { namespace e300 { namespace radio { -#define localparam static const int +static UHD_INLINE uint32_t sr_addr(const uint32_t offset) +{ + return offset * 4; +} + +static const uint32_t DACSYNC = 5; +static const uint32_t LOOPBACK = 6; +static const uint32_t TEST = 7; +static const uint32_t SPI = 8; +static const uint32_t GPIO = 16; +static const uint32_t MISC_OUTS = 24; +static const uint32_t READBACK = 32; +static const uint32_t TX_CTRL = 64; +static const uint32_t RX_CTRL = 96; +static const uint32_t TIME = 128; +static const uint32_t RX_DSP = 144; +static const uint32_t TX_DSP = 184; +static const uint32_t LEDS = 195; +static const uint32_t FP_GPIO = 200; +static const uint32_t RX_FRONT = 208; +static const uint32_t TX_FRONT = 216; +static const uint32_t CODEC_IDLE = 250; -localparam SR_TEST = 7; -localparam SR_SPI = 8; -localparam SR_GPIO = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK = 32; -localparam SR_TX_CTRL = 64; -localparam SR_RX_CTRL = 96; -localparam SR_TIME = 128; -localparam SR_RX_DSP = 144; -localparam SR_TX_DSP = 184; -localparam SR_LEDS = 195; -localparam SR_FP_GPIO = 200; -localparam SR_RX_FRONT = 208; -localparam SR_TX_FRONT = 216; -localparam SR_CODEC_IDLE = 250; +static const uint32_t RB32_GPIO = 0; +static const uint32_t RB32_SPI = 4; +static const uint32_t RB64_TIME_NOW = 8; +static const uint32_t RB64_TIME_PPS = 16; +static const uint32_t RB32_TEST = 24; +static const uint32_t RB32_RX = 28; +static const uint32_t RB32_FP_GPIO = 32; +static const uint32_t RB32_MISC_INS = 36; +static const uint32_t RB64_CODEC_READBACK = 40; +static const uint32_t RB32_RADIO_NUM = 48; +}}}} // namespace -localparam RB32_SPI = 4; -localparam RB64_TIME_NOW = 8; -localparam RB64_TIME_PPS = 16; -localparam RB32_TEST = 24; -localparam RB32_FP_GPIO = 32; -localparam RB64_CODEC_READBACK = 40; -localparam RB32_RADIO_NUM = 48; +#define localparam static const int localparam ST_RX_ENABLE = 20; localparam ST_TX_ENABLE = 19; diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp index 6742f5f86..be98f4027 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -130,10 +130,123 @@ public: _args.bits = uhd::htonx<boost::uint32_t>(0); _transact(); - return sensor_value_t("RSSI", _retval.rssi, "dB"); } + sensor_value_t get_temperature() + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_GET_TEMPERATURE); + _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE); /*Unused*/ + _args.bits = uhd::htonx<boost::uint32_t>(0); + + _transact(); + return sensor_value_t("temp", _retval.temp, "C"); + } + + void set_dc_offset_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_dc_correction = on ? 1 : 0; + + _transact(); + } + + void set_iq_balance_auto(const std::string &which, const bool on) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_iq_correction = on ? 1 : 0; + + _transact(); + } + + void set_agc(const std::string &which, bool enable) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.use_agc = enable ? 1 : 0; + + _transact(); + } + + void set_agc_mode(const std::string &which, const std::string &mode) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC_MODE); + + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + + if(mode == "slow") { + _args.agc_mode = 0; + } else if (mode == "fast") { + _args.agc_mode = 1; + } else { + throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode."); + } + + _transact(); + } + + //! set the filter bandwidth for the frontend's analog low pass + double set_bw_filter(const std::string &which, const double bw) + { + _clear(); + _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_BW); + if (which == "TX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); + else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); + else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); + else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); + else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + _args.bw = bw; + + _transact(); + return _retval.bw; + } + + //! List all available filters by name + std::vector<std::string> get_filter_names(const std::string &) + { + return std::vector<std::string>(); + } + + //! Return a list of all filters + filter_info_base::sptr get_filter(const std::string &, const std::string &) + { + UHD_THROW_INVALID_CODE_PATH(); + } + + //! Write back a filter + void set_filter(const std::string &, const std::string &, const filter_info_base::sptr) + { + UHD_MSG(warning) << "Attempting to set filter on E300 in network mode." << std::endl; + } + + void output_digital_test_tone(bool enb) + { + UHD_THROW_INVALID_CODE_PATH(); + } + private: void _transact() { { diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp index e21f2ef95..43723e0d5 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp @@ -34,6 +34,12 @@ public: double gain; double freq; double rssi; + double temp; + double bw; + boost::uint32_t use_dc_correction; + boost::uint32_t use_iq_correction; + boost::uint32_t use_agc; + boost::uint32_t agc_mode; boost::uint64_t bits; }; @@ -44,7 +50,13 @@ public: static const boost::uint32_t ACTION_TUNE = 13; static const boost::uint32_t ACTION_SET_LOOPBACK = 14; static const boost::uint32_t ACTION_GET_RSSI = 15; - static const boost::uint32_t ACTION_GET_FREQ = 16; + static const boost::uint32_t ACTION_GET_TEMPERATURE = 16; + static const boost::uint32_t ACTION_SET_DC_OFFSET_AUTO = 17; + static const boost::uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18; + static const boost::uint32_t ACTION_SET_AGC = 19; + static const boost::uint32_t ACTION_SET_AGC_MODE = 20; + static const boost::uint32_t ACTION_SET_BW = 21; + static const boost::uint32_t ACTION_GET_FREQ = 22; //Values for "which" static const boost::uint32_t CHAIN_NONE = 0; diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp index 527cfb91a..a4319fa4b 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.cpp +++ b/host/lib/usrp/e300/e300_sensor_manager.cpp @@ -24,7 +24,6 @@ #include <cstring> #include <uhd/exception.hpp> #include <uhd/utils/byteswap.hpp> -#include <uhd/usrp/gps_ctrl.hpp> namespace uhd { namespace usrp { namespace e300 { @@ -38,17 +37,13 @@ public: std::vector<std::string> get_sensors() { - return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); + return boost::assign::list_of("temp")("ref_locked"); } uhd::sensor_value_t get_sensor(const std::string &key) { if (key == "temp") return get_mb_temp(); - else if (key == "gps_locked") - return get_gps_lock(); - else if (key == "gps_time") - return get_gps_time(); else if (key == "ref_locked") return get_ref_lock(); else @@ -94,108 +89,6 @@ public: "C"); } - uhd::sensor_value_t get_gps_time(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_TIME); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_TIME); - // TODO: Use proper serialization here ... - return sensor_value_t("GPS epoch time", int(uhd::ntohx<boost::uint32_t>(transaction.value)), "seconds"); - } - - bool get_gps_found(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_FOUND); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_FOUND); - // TODO: Use proper serialization here ... - return (uhd::ntohx(transaction.value) > 0); - } - - uhd::sensor_value_t get_gps_lock(void) - { - boost::mutex::scoped_lock(_mutex); - sensor_transaction_t transaction; - transaction.which = uhd::htonx<boost::uint32_t>(GPS_LOCK); - { - uhd::transport::managed_send_buffer::sptr buff - = _xport->get_send_buff(1.0); - if (not buff or buff->size() < sizeof(transaction)) { - throw uhd::runtime_error("sensor proxy send timeout"); - } - std::memcpy( - buff->cast<void *>(), - &transaction, - sizeof(transaction)); - buff->commit(sizeof(transaction)); - } - { - uhd::transport::managed_recv_buffer::sptr buff - = _xport->get_recv_buff(1.0); - - if (not buff or buff->size() < sizeof(transaction)) - throw uhd::runtime_error("sensor proxy recv timeout"); - - std::memcpy( - &transaction, - buff->cast<const void *>(), - sizeof(transaction)); - } - UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_LOCK); - // TODO: Use proper serialization here ... - return sensor_value_t("GPS lock status", (uhd::ntohx(transaction.value) > 0), "locked", "unlocked"); - } - uhd::sensor_value_t get_ref_lock(void) { boost::mutex::scoped_lock(_mutex); @@ -255,24 +148,20 @@ static const std::string E300_TEMP_SYSFS = "iio:device0"; class e300_sensor_local : public e300_sensor_manager { public: - e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) : - _gps_ctrl(gps_ctrl), _global_regs(global_regs) + e300_sensor_local(global_regs::sptr global_regs) : + _global_regs(global_regs) { } std::vector<std::string> get_sensors() { - return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); + return boost::assign::list_of("temp")("ref_locked"); } uhd::sensor_value_t get_sensor(const std::string &key) { if (key == "temp") return get_mb_temp(); - else if (key == "gps_locked") - return get_gps_lock(); - else if (key == "gps_time") - return get_gps_time(); else if (key == "ref_locked") return get_ref_lock(); else @@ -291,21 +180,6 @@ public: return sensor_value_t("temp", (raw + offset) * scale / 1000, "C"); } - bool get_gps_found(void) - { - return _gps_ctrl->gps_detected(); - } - - uhd::sensor_value_t get_gps_lock(void) - { - return _gps_ctrl->get_sensor("gps_locked"); - } - - uhd::sensor_value_t get_gps_time(void) - { - return _gps_ctrl->get_sensor("gps_time"); - } - uhd::sensor_value_t get_ref_lock(void) { //PPSLOOP_LOCKED_MASK is asserted in the following cases: @@ -322,22 +196,21 @@ public: } private: - gps_ctrl::sptr _gps_ctrl; global_regs::sptr _global_regs; }; }}} using namespace uhd::usrp::e300; e300_sensor_manager::sptr e300_sensor_manager::make_local( - uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) + global_regs::sptr global_regs) { - return sptr(new e300_sensor_local(gps_ctrl, global_regs)); + return sptr(new e300_sensor_local(global_regs)); } #else using namespace uhd::usrp::e300; e300_sensor_manager::sptr e300_sensor_manager::make_local( - uhd::gps_ctrl::sptr, global_regs::sptr) + global_regs::sptr) { throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE"); } diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp index 09f889251..bfaf8e90c 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.hpp +++ b/host/lib/usrp/e300/e300_sensor_manager.hpp @@ -39,25 +39,22 @@ struct sensor_transaction_t { -enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2, - GPS_LOCK=3, REF_LOCK=4}; +enum sensor {ZYNQ_TEMP=0, REF_LOCK=4}; class e300_sensor_manager : boost::noncopyable { public: typedef boost::shared_ptr<e300_sensor_manager> sptr; - virtual bool get_gps_found(void) = 0; virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0; virtual std::vector<std::string> get_sensors(void) = 0; virtual uhd::sensor_value_t get_mb_temp(void) = 0; - virtual uhd::sensor_value_t get_gps_lock(void) = 0; - virtual uhd::sensor_value_t get_gps_time(void) = 0; virtual uhd::sensor_value_t get_ref_lock(void) = 0; + static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport); - static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs); + static sptr make_local(global_regs::sptr global_regs); // Note: This is a hack static boost::uint32_t pack_float_in_uint32_t(const float &v) diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp deleted file mode 100644 index 8705d6c52..000000000 --- a/host/lib/usrp/e300/e300_ublox_control.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP - -#include <boost/cstdint.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -class control : public virtual uhd::gps_ctrl -{ -public: - typedef boost::shared_ptr<control> sptr; - - static sptr make(const std::string &node, const size_t baud_rate); - - virtual void configure_message_rate( - const boost::uint16_t msg, - const boost::uint8_t rate) = 0; - - virtual void configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins) = 0; - - virtual void configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay) = 0; - - virtual void configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref) = 0; -}; -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp deleted file mode 100644 index 389bf79fa..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.cpp +++ /dev/null @@ -1,505 +0,0 @@ -#include <boost/format.hpp> -#include <boost/foreach.hpp> -#include <boost/bind.hpp> -#include <boost/make_shared.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/assign/list_of.hpp> -#include "boost/date_time/posix_time/posix_time.hpp" - -#include <iostream> - - -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/exception.hpp> - -#include "e300_ublox_control.hpp" - -#ifdef E300_NATIVE -#include "e300_ublox_control_impl.hpp" - - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -control_impl::control_impl(const std::string &node, const size_t baud_rate) -{ - _decode_init(); - _serial = boost::make_shared<async_serial>(node, baud_rate); - _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2)); - - _detect(); - - configure_message_rate(MSG_GLL, 0); - configure_message_rate(MSG_GSV, 0); - configure_message_rate(MSG_GGA, 0); - configure_message_rate(MSG_GSA, 0); - configure_message_rate(MSG_RMC, 0); - configure_message_rate(MSG_VTG, 0); - configure_message_rate(MSG_NAV_TIMEUTC, 1); - configure_message_rate(MSG_NAV_SOL, 1); - - configure_antenna(0x001b, 0x8251); - - configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0); - - _sensors = boost::assign::list_of("gps_locked")("gps_time"); -} - -bool control_impl::gps_detected(void) -{ - return _detected; -} - -void control_impl::_detect(void) -{ - _send_message(MSG_MON_VER, NULL, 0); -} - -std::vector<std::string> control_impl::get_sensors(void) -{ - return _sensors; -} - -uhd::sensor_value_t control_impl::get_sensor(std::string key) -{ - if (key == "gps_time") { - bool lock; - _locked.wait_and_see(lock); - return sensor_value_t("GPS epoch time", - lock ? int(_get_epoch_time()) : 0, "seconds"); - } else if (key == "gps_locked") { - bool lock; - _locked.wait_and_see(lock); - return sensor_value_t("GPS lock status", lock, "locked", "unlocked"); - } else - throw uhd::key_error(str(boost::format("sensor %s unknown.") % key)); -} - -std::time_t control_impl::_get_epoch_time(void) -{ - boost::posix_time::ptime ptime; - _ptime.wait_and_see(ptime); - return (ptime - boost::posix_time::from_time_t(0)).total_seconds(); -} - -control_impl::~control_impl(void) -{ -} - -void control_impl::_decode_init(void) -{ - _decode_state = DECODE_SYNC1; - _rx_ck_a = 0; - _rx_ck_b = 0; - _rx_payload_length = 0; - _rx_payload_index = 0; -} - -void control_impl::_add_byte_to_checksum(const boost::uint8_t b) -{ - _rx_ck_a = _rx_ck_a + b; - _rx_ck_b = _rx_ck_b + _rx_ck_a; -} - -void control_impl::_calc_checksum( - const boost::uint8_t *buffer, - const boost::uint16_t length, - checksum_t &checksum) -{ - for (size_t i = 0; i < length; i++) - { - checksum.ck_a = checksum.ck_a + buffer[i]; - checksum.ck_b = checksum.ck_b + checksum.ck_a; - } -} - -void control_impl::configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref) -{ - payload_tx_cfg_rate_t cfg_rate; - cfg_rate.meas_rate = uhd::htowx<boost::uint16_t>(meas_rate); - cfg_rate.nav_rate = uhd::htowx<boost::uint16_t>(nav_rate); - cfg_rate.time_ref = uhd::htowx<boost::uint16_t>(time_ref); - - _send_message( - MSG_CFG_RATE, - reinterpret_cast<const uint8_t*>(&cfg_rate), - sizeof(cfg_rate)); - - _wait_for_ack(MSG_CFG_RATE, 1.0); -} - -void control_impl::configure_message_rate( - const boost::uint16_t msg, - const uint8_t rate) -{ - payload_tx_cfg_msg_t cfg_msg; - cfg_msg.msg = uhd::htowx<boost::uint16_t>(msg); - cfg_msg.rate[0] = 0;//rate; - cfg_msg.rate[1] = rate; - cfg_msg.rate[2] = 0;//rate; - cfg_msg.rate[3] = 0;//rate; - cfg_msg.rate[4] = 0;//rate; - cfg_msg.rate[5] = 0;//rate; - _send_message( - MSG_CFG_MSG, - reinterpret_cast<const uint8_t*>(&cfg_msg), - sizeof(cfg_msg)); - - _wait_for_ack(MSG_CFG_MSG, 1.0); -} - -void control_impl::configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins) -{ - payload_tx_cfg_ant_t cfg_ant; - cfg_ant.pins = uhd::htowx<boost::uint16_t>(pins); - cfg_ant.flags = uhd::htowx<boost::uint16_t>(flags); - _send_message( - MSG_CFG_ANT, - reinterpret_cast<const uint8_t*>(&cfg_ant), - sizeof(cfg_ant)); - if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) { - throw uhd::runtime_error("Didn't get an ACK for antenna configuration."); - } - -} - -void control_impl::configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay) -{ - payload_tx_cfg_tp_t cfg_tp; - cfg_tp.interval = uhd::htowx<boost::uint32_t>(interval); - cfg_tp.length = uhd::htowx<boost::uint32_t>(length); - cfg_tp.status = status; - cfg_tp.time_ref = time_ref; - cfg_tp.flags = flags; - cfg_tp.antenna_delay = uhd::htowx<boost::int16_t>(antenna_delay); - cfg_tp.rf_group_delay = uhd::htowx<boost::int16_t>(rf_group_delay); - cfg_tp.user_delay = uhd::htowx<boost::int32_t>(user_delay); - _send_message( - MSG_CFG_TP, - reinterpret_cast<const uint8_t*>(&cfg_tp), - sizeof(cfg_tp)); - if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) { - throw uhd::runtime_error("Didn't get an ACK for PPS configuration."); - } -} - - -void control_impl::_rx_callback(const char *data, unsigned int len) -{ - //std::cout << "IN RX CALLBACK" << std::flush << std::endl; - std::vector<char> v(data, data+len); - BOOST_FOREACH(const char &c, v) - { - _parse_char(c); - } -} - -void control_impl::_parse_char(const boost::uint8_t b) -{ - int ret = 0; - - switch (_decode_state) { - - // we're expecting the first sync byte - case DECODE_SYNC1: - if (b == SYNC1) { // sync1 found goto next step - _decode_state = DECODE_SYNC2; - } // else stay around - break; - - // we're expecting the second sync byte - case DECODE_SYNC2: - if (b == SYNC2) { // sync2 found goto next step - _decode_state = DECODE_CLASS; - } else { - // failed, reset - _decode_init(); - } - break; - - // we're expecting the class byte - case DECODE_CLASS: - _add_byte_to_checksum(b); - _rx_msg = b; - _decode_state = DECODE_ID; - break; - - // we're expecting the id byte - case DECODE_ID: - _add_byte_to_checksum(b); - _rx_msg |= (b << 8); - _decode_state = DECODE_LENGTH1; - break; - - // we're expecting the first length byte - case DECODE_LENGTH1: - _add_byte_to_checksum(b); - _rx_payload_length = b; - _decode_state = DECODE_LENGTH2; - break; - - // we're expecting the second length byte - case DECODE_LENGTH2: - _add_byte_to_checksum(b); - _rx_payload_length |= (b << 8); - if(_payload_rx_init()) { - _decode_init(); // we failed, give up for this one - } else { - _decode_state = _rx_payload_length ? - DECODE_PAYLOAD : DECODE_CHKSUM1; - } - break; - - // we're expecting payload - case DECODE_PAYLOAD: - _add_byte_to_checksum(b); - switch(_rx_msg) { - default: - ret = _payload_rx_add(b); - break; - }; - if (ret < 0) { - // we couldn't deal with the payload, discard the whole thing - _decode_init(); - } else if (ret > 0) { - // payload was complete, let's check the checksum; - _decode_state = DECODE_CHKSUM1; - } else { - // more payload expected, don't move - } - ret = 0; - break; - - case DECODE_CHKSUM1: - if (_rx_ck_a != b) { - // checksum didn't match, barf - std::cout << boost::format("Failed checksum byte1 %lx != %lx") - % int(_rx_ck_a) % int(b) << std::endl; - _decode_init(); - } else { - _decode_state = DECODE_CHKSUM2; - } - break; - - case DECODE_CHKSUM2: - if (_rx_ck_b != b) { - // checksum didn't match, barf - std::cout << boost::format("Failed checksum byte2 %lx != %lx") - % int(_rx_ck_b) % int(b) << std::endl; - - } else { - ret = _payload_rx_done(); // payload done - } - _decode_init(); - break; - - default: - break; - }; -} - -int control_impl::_payload_rx_init(void) -{ - int ret = 0; - - _rx_state = RXMSG_HANDLE; // by default handle - switch(_rx_msg) { - - case MSG_NAV_SOL: - if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_NAV_TIMEUTC: - if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_MON_VER: - break; // always take this one - - case MSG_ACK_ACK: - if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - case MSG_ACK_NAK: - if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t))) - _rx_state = RXMSG_ERROR_LENGTH; - break; - - default: - _rx_state = RXMSG_DISABLE; - break; - }; - - switch (_rx_state) { - case RXMSG_HANDLE: // handle message - case RXMSG_IGNORE: // ignore message but don't report error - ret = 0; - break; - case RXMSG_DISABLE: // ignore message but don't report error - case RXMSG_ERROR_LENGTH: // the length doesn't match - ret = -1; - break; - default: // invalid, error - ret = -1; - break; - }; - - return ret; -} - -int control_impl::_payload_rx_add(const boost::uint8_t b) -{ - int ret = 0; - _buf.raw[_rx_payload_index] = b; - if (++_rx_payload_index >= _rx_payload_length) - ret = 1; - return ret; -} - -int control_impl::_payload_rx_done(void) -{ - int ret = 0; - if (_rx_state != RXMSG_HANDLE) { - return 0; - } - - switch (_rx_msg) { - case MSG_MON_VER: - _detected = true; - break; - - case MSG_MON_HW: - std::cout << "MON-HW" << std::endl; - break; - - case MSG_ACK_ACK: - if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg)) - _ack_state = ACK_GOT_ACK; - break; - - case MSG_ACK_NAK: - if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg)) - _ack_state = ACK_GOT_NAK; - - break; - - case MSG_CFG_ANT: - break; - - case MSG_NAV_TIMEUTC: - _ptime.update(boost::posix_time::ptime( - boost::gregorian::date( - boost::gregorian::greg_year(uhd::wtohx<boost::uint16_t>( - _buf.payload_rx_nav_timeutc.year)), - boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month), - boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)), - (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour) - + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min) - + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec)))); - break; - - case MSG_NAV_SOL: - _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0); - break; - - default: - std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg); - for(size_t i = 0; i < _rx_payload_length; i++) - std::cout << boost::format("%lx, ") % int(_buf.raw[i]); - std::cout << "]"<< std::endl; - break; - }; - return ret; -} - -void control_impl::_send_message( - const boost::uint16_t msg, - const boost::uint8_t *payload, - const boost::uint16_t len) -{ - header_t header = {SYNC1, SYNC2, msg, len}; - checksum_t checksum = {0, 0}; - - // calculate checksums, first header without sync - // then payload - _calc_checksum( - reinterpret_cast<boost::uint8_t*>(&header) + 2, - sizeof(header) - 2, checksum); - if (payload) - _calc_checksum(payload, len, checksum); - - _serial->write( - reinterpret_cast<const char*>(&header), - sizeof(header)); - - if (payload) - _serial->write((const char *) payload, len); - - _serial->write( - reinterpret_cast<const char*>(&checksum), - sizeof(checksum)); -} - -int control_impl::_wait_for_ack( - const boost::uint16_t msg, - const double timeout) -{ - int ret = -1; - - _ack_state = ACK_WAITING; - _ack_waiting_msg = msg; - - boost::system_time timeout_time = - boost::get_system_time() + - boost::posix_time::milliseconds(timeout * 1000.0); - - do { - if(_ack_state == ACK_GOT_ACK) - return 0; - else if (_ack_state == ACK_GOT_NAK) { - return -1; - } - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - } while (boost::get_system_time() < timeout_time); - - // we get here ... it's a timeout - _ack_state = ACK_IDLE; - return ret; -} - - -}} // namespace ublox::ubx -}}} // namespace - -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string &node, const size_t baud_rate) -{ - return control::sptr(new control_impl(node, baud_rate)); -} -#else -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string& /* node */, const size_t /* baud_rate */) -{ - throw uhd::assertion_error("control::sptr::make: !E300_NATIVE"); -} -#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp deleted file mode 100644 index a1dcbfe6c..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.hpp +++ /dev/null @@ -1,457 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP - -#include <boost/cstdint.hpp> -#include <boost/noncopyable.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { -// ublox binary sync words -static const boost::uint8_t SYNC1 = 0xB5; -static const boost::uint8_t SYNC2 = 0x62; - -// message classes -static const boost::uint8_t CLASS_NAV = 0x01; -static const boost::uint8_t CLASS_ACK = 0x05; -static const boost::uint8_t CLASS_CFG = 0x06; -static const boost::uint8_t CLASS_MON = 0x0a; -static const boost::uint8_t CLASS_NMEA = 0xf0; - -// Message IDs -static const boost::uint8_t ID_NAV_POSLLH = 0x02; -static const boost::uint8_t ID_NAV_SOL = 0x06; -static const boost::uint8_t ID_NAV_PVT = 0x07; -static const boost::uint8_t ID_NAV_VELNED = 0x12; -static const boost::uint8_t ID_NAV_TIMEUTC = 0x21; -static const boost::uint8_t ID_NAV_SVINFO = 0x30; -static const boost::uint8_t ID_ACK_NAK = 0x00; -static const boost::uint8_t ID_ACK_ACK = 0x01; -static const boost::uint8_t ID_CFG_PRT = 0x00; -static const boost::uint8_t ID_CFG_ANT = 0x13; -static const boost::uint8_t ID_CFG_TP = 0x07; -static const boost::uint8_t ID_CFG_MSG = 0x01; -static const boost::uint8_t ID_CFG_RATE = 0x08; -static const boost::uint8_t ID_CFG_NAV5 = 0x24; -static const boost::uint8_t ID_MON_VER = 0x04; -static const boost::uint8_t ID_MON_HW = 0x09; -static const boost::uint8_t ID_GGA = 0x00; -static const boost::uint8_t ID_GLL = 0x01; -static const boost::uint8_t ID_GSA = 0x02; -static const boost::uint8_t ID_GSV = 0x03; -static const boost::uint8_t ID_RMC = 0x04; -static const boost::uint8_t ID_VTG = 0x05; -static const boost::uint8_t ID_GST = 0x07; - -// Message Classes & IDs // -static const boost::uint16_t MSG_NAV_POSLLH - = CLASS_NAV | (ID_NAV_POSLLH << 8); -static const boost::uint16_t MSG_NAV_SOL - = CLASS_NAV | (ID_NAV_SOL << 8); -static const boost::uint16_t MSG_NAV_PVT - = CLASS_NAV | (ID_NAV_PVT << 8); -static const boost::uint16_t MSG_NAV_VELNED - = CLASS_NAV | (ID_NAV_VELNED << 8); -static const boost::uint16_t MSG_NAV_TIMEUTC - = CLASS_NAV | (ID_NAV_TIMEUTC << 8); -static const boost::uint16_t MSG_NAV_SVINFO - = CLASS_NAV | (ID_NAV_SVINFO << 8); -static const boost::uint16_t MSG_ACK_NAK - = CLASS_ACK | (ID_ACK_NAK << 8); -static const boost::uint16_t MSG_ACK_ACK - = CLASS_ACK | (ID_ACK_ACK << 8); -static const boost::uint16_t MSG_CFG_PRT - = CLASS_CFG | (ID_CFG_PRT << 8); -static const boost::uint16_t MSG_CFG_ANT - = CLASS_CFG | (ID_CFG_ANT << 8); -static const boost::uint16_t MSG_CFG_TP - = CLASS_CFG | (ID_CFG_TP << 8); -static const boost::uint16_t MSG_CFG_MSG - = CLASS_CFG | (ID_CFG_MSG << 8); -static const boost::uint16_t MSG_CFG_RATE - = CLASS_CFG | (ID_CFG_RATE << 8); -static const boost::uint16_t MSG_CFG_NAV5 - = CLASS_CFG | (ID_CFG_NAV5 << 8); -static const boost::uint16_t MSG_MON_HW - = CLASS_MON | (ID_MON_HW << 8); -static const boost::uint16_t MSG_MON_VER - = CLASS_MON | (ID_MON_VER << 8); - -// NMEA ones -static const boost::uint16_t MSG_GGA - = CLASS_NMEA | (ID_GGA << 8); -static const boost::uint16_t MSG_GLL - = CLASS_NMEA | (ID_GLL << 8); -static const boost::uint16_t MSG_GSA - = CLASS_NMEA | (ID_GSA << 8); -static const boost::uint16_t MSG_GSV - = CLASS_NMEA | (ID_GSV << 8); -static const boost::uint16_t MSG_RMC - = CLASS_NMEA | (ID_RMC << 8); -static const boost::uint16_t MSG_VTG - = CLASS_NMEA | (ID_VTG << 8); - -// header -struct header_t -{ - boost::uint8_t sync1; - boost::uint8_t sync2; - boost::uint16_t msg; - boost::uint16_t length; -}; - -// checksum -struct checksum_t -{ - boost::uint8_t ck_a; - boost::uint8_t ck_b; -}; - -// rx rx mon-hw (ubx6) -struct payload_rx_mon_hw_t -{ - boost::uint32_t pin_sel; - boost::uint32_t pin_bank; - boost::uint32_t pin_dir; - boost::uint32_t pin_val; - boost::uint16_t noise_per_ms; - boost::uint16_t agc_cnt; - boost::uint8_t a_status; - boost::uint8_t a_power; - boost::uint8_t flags; - boost::uint8_t reserved1; - boost::uint32_t used_mask; - boost::uint8_t vp[25]; - boost::uint8_t jam_ind; - boost::uint16_t reserved3; - boost::uint32_t pin_irq; - boost::uint32_t pullh; - boost::uint32_t pulll; -}; - -// rx mon-ver -struct payload_rx_mon_ver_part1_t -{ - char sw_version[30]; - char hw_version[10]; -}; - -struct payload_rx_mon_ver_part2_t -{ - boost::uint8_t extension[30]; -}; - -// rx ack-ack -typedef union { - boost::uint16_t msg; - struct { - boost::uint8_t cls_id; - boost::uint8_t msg_id; - }; -} payload_rx_ack_ack_t; - -// rx ack-nak -typedef union { - boost::uint16_t msg; - struct { - boost::uint8_t cls_id; - boost::uint8_t msg_id; - }; -} payload_rx_ack_nak_t; - -// tx cfg-prt (uart) -struct payload_tx_cfg_prt_t -{ - boost::uint8_t port_id; - boost::uint8_t reserved0; - boost::uint16_t tx_ready; - boost::uint32_t mode; - boost::uint32_t baud_rate; - boost::uint16_t in_proto_mask; - boost::uint16_t out_proto_mask; - boost::uint16_t flags; - boost::uint16_t reserved5; -}; - -// tx cfg-rate -struct payload_tx_cfg_rate_t -{ - boost::uint16_t meas_rate; - boost::uint16_t nav_rate; - boost::uint16_t time_ref; -}; - -// tx cfg-msg -struct payload_tx_cfg_msg_t -{ - boost::uint16_t msg; - boost::uint8_t rate[6]; -}; - - -// tx cfg-ant -struct payload_tx_cfg_ant_t -{ - boost::uint16_t flags; - boost::uint16_t pins; -}; - -// tx cfg-tp -struct payload_tx_cfg_tp_t -{ - boost::uint32_t interval; - boost::uint32_t length; - boost::int8_t status; - boost::uint8_t time_ref; - boost::uint8_t flags; - boost::uint8_t reserved1; - boost::int16_t antenna_delay; - boost::int16_t rf_group_delay; - boost::int32_t user_delay; -}; - -struct payload_rx_nav_sol_t -{ - boost::uint32_t i_tow; - boost::int32_t f_tow; - boost::int16_t week; - boost::uint8_t gps_fix; - boost::uint8_t flags; - boost::int32_t ecef_x; - boost::int32_t ecef_y; - boost::int32_t ecef_z; - boost::uint32_t p_acc; - boost::int32_t ecef_vx; - boost::int32_t ecef_vy; - boost::int32_t ecef_vz; - boost::uint32_t s_acc; - boost::uint16_t p_dop; - boost::uint8_t reserved1; - boost::uint8_t num_sv; - boost::uint32_t reserved2; -}; - -struct payload_rx_nav_timeutc_t -{ - boost::uint32_t i_tow; - boost::uint32_t t_acc; - boost::int32_t nano; - boost::uint16_t year; - boost::uint8_t month; - boost::uint8_t day; - boost::uint8_t hour; - boost::uint8_t min; - boost::uint8_t sec; - boost::uint8_t valid; -}; - -typedef union { - payload_rx_mon_hw_t payload_rx_mon_hw; - - payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1; - payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2; - - payload_rx_ack_ack_t payload_rx_ack_ack; - payload_rx_ack_nak_t payload_rx_ack_nak; - - payload_tx_cfg_prt_t payload_tx_cfg_prt; - payload_tx_cfg_ant_t payload_tx_cfg_ant; - payload_tx_cfg_rate_t payload_tx_cfg_rate; - - payload_tx_cfg_msg_t payload_tx_cfg_msg; - - payload_rx_nav_timeutc_t payload_rx_nav_timeutc; - payload_rx_nav_sol_t payload_rx_nav_sol; - boost::uint8_t raw[]; -} buf_t; - - -template <typename T> -class sensor_entry -{ -public: - sensor_entry() : _seen(false) - { - } - - void update(const T &val) - { - boost::mutex::scoped_lock l(_mutex); - _value = val; - _seen = false; - l.unlock(); - _cond.notify_one(); - } - - bool seen() const - { - boost::mutex::scoped_lock l(_mutex); - return _seen; - } - - bool try_and_see(T &val) - { - boost::mutex::scoped_lock l(_mutex); - if (_seen) - return false; - - val = _value; - _seen = true; - return true; - } - - void wait_and_see(T &val) - { - boost::mutex::scoped_lock l(_mutex); - while(_seen) - { - _cond.wait(l); - //std::cout << "Already seen ... " << std::endl; - } - val = _value; - _seen = true; - } - -private: // members - T _value; - boost::mutex _mutex; - boost::condition_variable _cond; - bool _seen; -}; - -class control_impl : public control -{ -public: - control_impl(const std::string &node, const size_t baud_rate); - - virtual ~control_impl(void); - - void configure_message_rate( - const boost::uint16_t msg, - const boost::uint8_t rate); - - void configure_antenna( - const boost::uint16_t flags, - const boost::uint16_t pins); - - void configure_pps( - const boost::uint32_t interval, - const boost::uint32_t length, - const boost::int8_t status, - const boost::uint8_t time_ref, - const boost::uint8_t flags, - const boost::int16_t antenna_delay, - const boost::int16_t rf_group_delay, - const boost::int32_t user_delay); - - void configure_rates( - boost::uint16_t meas_rate, - boost::uint16_t nav_rate, - boost::uint16_t time_ref); - - // gps_ctrl interface - bool gps_detected(void); - std::vector<std::string> get_sensors(void); - uhd::sensor_value_t get_sensor(std::string key); - -private: // types - enum decoder_state_t { - DECODE_SYNC1 = 0, - DECODE_SYNC2, - DECODE_CLASS, - DECODE_ID, - DECODE_LENGTH1, - DECODE_LENGTH2, - DECODE_PAYLOAD, - DECODE_CHKSUM1, - DECODE_CHKSUM2, - }; - - enum rxmsg_state_t { - RXMSG_IGNORE = 0, - RXMSG_HANDLE, - RXMSG_DISABLE, - RXMSG_ERROR_LENGTH - }; - - enum ack_state_t { - ACK_IDLE = 0, - ACK_WAITING, - ACK_GOT_ACK, - ACK_GOT_NAK - }; - -private: // methods - std::time_t _get_epoch_time(void); - - void _decode_init(void); - - void _add_byte_to_checksum(const boost::uint8_t b); - - void _detect(void); - - void _send_message( - const boost::uint16_t msg, - const boost::uint8_t *payload, - const boost::uint16_t len); - - int _wait_for_ack( - const boost::uint16_t msg, - const double timeout); - - void _calc_checksum( - const boost::uint8_t *buffer, - const boost::uint16_t length, - checksum_t &checksum); - - void _rx_callback(const char *data, unsigned len); - - void _parse_char(const boost::uint8_t b); - - int _payload_rx_init(void); - - int _payload_rx_add(const boost::uint8_t b); - - int _payload_rx_done(void); - -private: // members - // gps_ctrl stuff - bool _detected; - std::vector<std::string> _sensors; - - sensor_entry<bool> _locked; - sensor_entry<boost::posix_time::ptime> _ptime; - - // decoder state - decoder_state_t _decode_state; - rxmsg_state_t _rxmsg_state; - - ack_state_t _ack_state; - boost::uint16_t _ack_waiting_msg; - - boost::uint8_t _rx_ck_a; - boost::uint8_t _rx_ck_b; - - boost::uint16_t _rx_payload_length; - size_t _rx_payload_index; - boost::uint16_t _rx_msg; - - rxmsg_state_t _rx_state; - - boost::shared_ptr<async_serial> _serial; - - // this has to be at the end of the - // class to be valid C++ - buf_t _buf; -}; - -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index b9d5128bb..207ef10ab 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2010-2011,2014-2015 Ettus Research LLC // // 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 @@ -157,7 +157,7 @@ public: _send("HAAAY GUYYYYS\n"); //to elicit a response from the GPSDO //wait for _send(...) to return - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); //then we loop until we either timeout, or until we get a response that indicates we're a JL device const boost::system_time comm_timeout = boost::get_system_time() + milliseconds(GPS_COMM_TIMEOUT_MS); @@ -237,19 +237,19 @@ private: //issue some setup stuff so it spits out the appropriate data //none of these should issue replies so we don't bother looking for them //we have to sleep between commands because the JL device, despite not acking, takes considerable time to process each command. - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SYST:COMM:SER:ECHO OFF\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SYST:COMM:SER:PRO OFF\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GPGGA 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GGAST 0\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("GPS:GPRMC 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); _send("SERV:TRAC 0\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); } //retrieve a raw NMEA sentence @@ -352,7 +352,7 @@ private: //enable servo reporting _send("SERV:TRAC 1\n"); - sleep(milliseconds(GPSDO_STUPID_DELAY_MS)); + sleep(milliseconds(GPSDO_COMMAND_DELAY_MS)); std::string reply; @@ -399,7 +399,7 @@ private: static const int GPS_SERVO_FRESHNESS = 2500; static const int GPS_LOCK_FRESHNESS = 2500; static const int GPS_TIMEOUT_DELAY_MS = 200; - static const int GPSDO_STUPID_DELAY_MS = 200; + static const int GPSDO_COMMAND_DELAY_MS = 200; }; /*********************************************************************** diff --git a/host/lib/usrp/gpsd_iface.cpp b/host/lib/usrp/gpsd_iface.cpp new file mode 100644 index 000000000..e0a1dea05 --- /dev/null +++ b/host/lib/usrp/gpsd_iface.cpp @@ -0,0 +1,309 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <cmath> + +#include <gps.h> + +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> +#include "boost/date_time/gregorian/gregorian.hpp" +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/fpclassify.hpp> + +#include <uhd/exception.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/dict.hpp> + +#include "gpsd_iface.hpp" + +namespace uhd { namespace usrp { + +static const size_t TIMEOUT = 240; +static const size_t CLICK_RATE = 250000; + +class gpsd_iface_impl : public virtual gpsd_iface { +public: + gpsd_iface_impl(const std::string &addr, boost::uint16_t port) + : _detected(false), _bthread(), _timeout_cnt(0) + { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + if (gps_open(addr.c_str(), + str(boost::format("%u") % port).c_str(), + &_gps_data) < 0) { + throw uhd::runtime_error( + str((boost::format("Failed to connect to gpsd: %s") + % gps_errstr(errno)))); + } + + // register for updates, we don't specify a specific device, + // therefore no WATCH_DEVICE + gps_stream(&_gps_data, WATCH_ENABLE, NULL); + + // create background thread talking to gpsd + boost::thread t(boost::bind(&gpsd_iface_impl::_thread_fcn ,this)); + _bthread.swap(t); + + + _sensors = boost::assign::list_of("gps_locked")("gps_time") \ + ("gps_position")("gps_gpgga")("gps_gprmc"); + } + + virtual ~gpsd_iface_impl(void) + { + // interrupt the background thread and wait for it to finish + _bthread.interrupt(); + _bthread.join(); + + // clean up ... + { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + gps_stream(&_gps_data, WATCH_DISABLE, NULL); + gps_close(&_gps_data); + } + } + + uhd::sensor_value_t get_sensor(std::string key) + { + if (key == "gps_locked") { + return sensor_value_t( + "GPS lock status", _gps_locked(), "locked", "unlocked"); + } else if (key == "gps_time") { + return sensor_value_t( + "GPS epoch time", int(_epoch_time()), "seconds"); + } else if (key == "gps_gpgga") { + return sensor_value_t( + "GPGGA", _gps_gpgga(), ""); + } else if (key == "gps_gprmc") { + return sensor_value_t( + "GPRMC", _gps_gprmc(), ""); + } else if (key == "gps_position") { + return sensor_value_t( + "GPS Position", str( + boost::format("%s %s %s") + % _gps_position()["lat"] + % _gps_position()["lon"] + % _gps_position()["alt"]), "lat/lon/alt"); + } else + throw uhd::key_error( + str(boost::format("sensor %s unknown.") % key)); + } + + bool gps_detected(void) { return _detected; }; + + std::vector<std::string> get_sensors(void) { return _sensors; }; + +private: // member functions + void _thread_fcn() + { + while (not boost::this_thread::interruption_requested()) { + if (!gps_waiting(&_gps_data, CLICK_RATE)) { + if (TIMEOUT < _timeout_cnt++) + _detected = false; + } else { + boost::unique_lock<boost::shared_mutex> l(_d_mutex); + + _timeout_cnt = 0; + _detected = true; + + if (gps_read(&_gps_data) < 0) + throw std::runtime_error("error while reading"); + } + } + } + + bool _gps_locked(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return _gps_data.fix.mode >= MODE_2D; + } + + std::time_t _epoch_time(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return (boost::posix_time::from_time_t(_gps_data.fix.time) + - boost::posix_time::from_time_t(0)).total_seconds(); + } + + boost::gregorian::date _gregorian_date(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + return boost::posix_time::from_time_t(_gps_data.fix.time).date(); + } + + uhd::dict<std::string, std::string> _gps_position(void) + { + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + uhd::dict<std::string, std::string> tmp; + if (_gps_data.fix.mode >= MODE_2D) { + tmp["lon"] = str(boost::format("%f deg") + % _gps_data.fix.longitude); + tmp["lat"] = str(boost::format("%f deg") + % _gps_data.fix.latitude); + tmp["alt"] = str(boost::format("%fm") + % _gps_data.fix.altitude); + } else { + tmp["lon"] = "n/a"; + tmp["lat"] = "n/a"; + tmp["alt"] = "n/a"; + } + return tmp; + } + + float _zeroize(float x) + { + return boost::math::isnan(x) ? 0.0 : x; + } + + int _nmea_checksum(const std::string &s) + { + if ((s.at(0) != '$')) + return 0; + + boost::uint8_t sum = '\0'; + for (size_t i = 1; i < s.size(); i++) + sum ^= static_cast<boost::uint8_t>(s.at(i)); + + return sum; + } + + double _deg_to_dm(double angle) + { + double fraction, integer; + fraction = std::modf(angle, &integer); + return std::floor(angle) * 100 + fraction * 60; + } + + std::string _gps_gprmc(void) + { + struct tm tm; + time_t intfixtime; + + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + tm.tm_mday = tm.tm_mon = tm.tm_year = 0; + tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + + if (boost::math::isnan(_gps_data.fix.time) == 0) { + intfixtime = (time_t) _gps_data.fix.time; + (void)gmtime_r(&intfixtime, &tm); + tm.tm_mon++; + tm.tm_year %= 100; + } + std::string string = str(boost::format( + "$GPRMC,%02d%02d%02d,%c,%09.4f,%c,%010.4f,%c,%.4f,%.3f,%02d%02d%02d,,") + % tm.tm_hour + % tm.tm_min + % tm.tm_sec + % (_gps_data.status ? 'A' : 'V') + % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.latitude))) + % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') + % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.longitude))) + % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') + % _zeroize(_gps_data.fix.speed * MPS_TO_KNOTS) + % _zeroize(_gps_data.fix.track) + % tm.tm_mday % tm.tm_mon % tm.tm_year); + + string.append(str( + boost::format("*%02X") % _nmea_checksum(string))); + + return string; + } + + std::string _gps_gpgga(void) + { + struct tm tm; + time_t intfixtime; + + // currently not supported, make it blank + float mag_var = NAN; + + boost::shared_lock<boost::shared_mutex> l(_d_mutex); + + intfixtime = (time_t) _gps_data.fix.time; + (void) gmtime_r(&intfixtime, &tm); + + std::string string = str(boost::format( + "$GPGGA,%02d%02d%02d,%09.4f,%c,%010.4f,%c,%d,%02d,") + % tm.tm_hour + % tm.tm_min + % tm.tm_sec + % _deg_to_dm(std::fabs(_gps_data.fix.latitude)) + % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') + % _deg_to_dm(std::fabs(_gps_data.fix.longitude)) + % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') + % _gps_data.status + % _gps_data.satellites_used); + + if (boost::math::isnan(_gps_data.dop.hdop)) + string.append(","); + else + string.append( + str(boost::format("%.2f,") % _gps_data.dop.hdop)); + + if (boost::math::isnan(_gps_data.fix.altitude)) + string.append(","); + else + string.append( + str(boost::format("%.2f,M,") % _gps_data.fix.altitude)); + + if (boost::math::isnan(_gps_data.separation)) + string.append(","); + else + string.append( + str(boost::format("%.3f,M,") % _gps_data.separation)); + + if (boost::math::isnan(mag_var)) + string.append(","); + else { + string.append( + str(boost::format("%3.2f,%s") % std::fabs(mag_var) + % (mag_var > 0 ? "E" : "W"))); + } + + string.append(str( + boost::format("*%02X") % _nmea_checksum(string))); + + return string; + } + +private: // members + std::vector<std::string> _sensors; + bool _detected; + + gps_data_t _gps_data; + boost::shared_mutex _d_mutex; + boost::thread _bthread; + size_t _timeout_cnt; +}; + +}} //namespace + +using namespace uhd::usrp; + +gpsd_iface::sptr gpsd_iface::make(const std::string &addr, const boost::uint16_t port) +{ + return gpsd_iface::sptr(new gpsd_iface_impl(addr, port)); +} diff --git a/host/lib/usrp/gpsd_iface.hpp b/host/lib/usrp/gpsd_iface.hpp new file mode 100644 index 000000000..7d934ae5c --- /dev/null +++ b/host/lib/usrp/gpsd_iface.hpp @@ -0,0 +1,36 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_GPSD_IFACE_HPP +#define INCLUDED_GPSD_IFACE_HPP + +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> + +#include <uhd/usrp/gps_ctrl.hpp> + +namespace uhd { namespace usrp { + +class gpsd_iface : public virtual uhd::gps_ctrl { +public: + typedef boost::shared_ptr<gpsd_iface> sptr; + static sptr make(const std::string &addr, boost::uint16_t port); +}; + +}}; + +#endif /* INCLUDED_GPSD_IFACE_HPP */ diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 68c084589..f60182c76 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2013,2015 Ettus Research LLC // // 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 @@ -16,6 +16,7 @@ // #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/asio/ip/address_v4.hpp> @@ -39,32 +40,6 @@ static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN; * Utility functions **********************************************************************/ -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ - byte_vector_t bytes; - for (size_t i = 0; i < std::min(string.size(), max_length); i++){ - bytes.push_back(string[i]); - } - if (bytes.size() < max_length - 1) bytes.push_back('\0'); - return bytes; -} - //! convert a string to a byte vector to write to eeprom static byte_vector_t string_to_uint16_bytes(const std::string &num_str){ const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str); @@ -238,7 +213,8 @@ struct x300_eeprom_map //indentifying numbers unsigned char revision[2]; unsigned char product[2]; - boost::uint8_t _pad0[4]; + unsigned char revision_compat[2]; + boost::uint8_t _pad0[2]; //all the mac addrs boost::uint8_t mac_addr0[6]; @@ -264,6 +240,11 @@ static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface) iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2) ); + //extract the revision compat number + mb_eeprom["revision_compat"] = uint16_bytes_to_string( + iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), 2) + ); + //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2) @@ -310,6 +291,12 @@ static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface) string_to_uint16_bytes(mb_eeprom["revision"]) ); + //parse the revision compat number + if (mb_eeprom.has_key("revision_compat")) iface.write_eeprom( + X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), + string_to_uint16_bytes(mb_eeprom["revision_compat"]) + ); + //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), diff --git a/host/lib/usrp/mboard_eeprom_c.cpp b/host/lib/usrp/mboard_eeprom_c.cpp new file mode 100644 index 000000000..8d5c069b9 --- /dev/null +++ b/host/lib/usrp/mboard_eeprom_c.cpp @@ -0,0 +1,72 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/mboard_eeprom.h> + +#include <uhd/exception.hpp> + +#include <string.h> + +uhd_error uhd_mboard_eeprom_make( + uhd_mboard_eeprom_handle* h +){ + UHD_SAFE_C( + *h = new uhd_mboard_eeprom_t; + ) +} + +uhd_error uhd_mboard_eeprom_free( + uhd_mboard_eeprom_handle* h +){ + UHD_SAFE_C( + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_mboard_eeprom_get_value( + uhd_mboard_eeprom_handle h, + const char* key, + char* value_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string value_cpp = h->mboard_eeprom_cpp.get(key); + strncpy(value_out, value_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_mboard_eeprom_set_value( + uhd_mboard_eeprom_handle h, + const char* key, + const char* value +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->mboard_eeprom_cpp[key] = value; + ) +} + +uhd_error uhd_mboard_eeprom_last_error( + uhd_mboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 794438b90..396237e24 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -26,10 +26,13 @@ #include <uhd/usrp/mboard_eeprom.hpp> #include <uhd/usrp/dboard_eeprom.hpp> #include <uhd/convert.hpp> +#include <uhd/utils/soft_register.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <algorithm> #include <cmath> using namespace uhd; @@ -69,7 +72,7 @@ static void do_samp_rate_warning_message( } } -static void do_tune_freq_results_message( +/*static void do_tune_freq_results_message( const tune_request_t &tune_req, const tune_result_t &tune_result, double actual_freq, @@ -171,7 +174,7 @@ static void do_tune_freq_results_message( UHD_MSG(warning) << results_string << std::endl; } -} +}*/ /*! The CORDIC can be used to shift the baseband below / past the tunable * limits of the actual RF front-end. The baseband filter, located on the @@ -309,7 +312,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ //-- Tune the RF frontend //------------------------------------------------------------------ - rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); + if (tune_request.rf_freq_policy != tune_request_t::POLICY_NONE) { + rf_fe_subtree->access<double>("freq/value").set(target_rf_freq); + } const double actual_rf_freq = rf_fe_subtree->access<double>("freq/value").get(); //------------------------------------------------------------------ @@ -346,7 +351,9 @@ static tune_result_t tune_xx_subdev_and_dsp( //------------------------------------------------------------------ //-- Tune the DSP //------------------------------------------------------------------ - dsp_subtree->access<double>("freq/value").set(target_dsp_freq); + if (tune_request.dsp_freq_policy != tune_request_t::POLICY_NONE) { + dsp_subtree->access<double>("freq/value").set(target_dsp_freq); + } const double actual_dsp_freq = dsp_subtree->access<double>("freq/value").get(); //------------------------------------------------------------------ @@ -431,6 +438,9 @@ public: ******************************************************************/ void set_master_clock_rate(double rate, size_t mboard){ if (mboard != ALL_MBOARDS){ + if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { + _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); + } _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate); return; } @@ -800,7 +810,7 @@ public: _tree->subtree(rx_dsp_root(chan)), _tree->subtree(rx_rf_fe_root(chan)), tune_request); - do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX"); + //do_tune_freq_results_message(tune_request, result, get_rx_freq(chan), "RX"); return result; } @@ -821,6 +831,26 @@ public: } void set_rx_gain(double gain, const std::string &name, size_t chan){ + /* Check if any AGC mode is enable and if so warn the user */ + if (chan != ALL_CHANS) { + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } else { + for (size_t c = 0; c < get_rx_num_channels(); c++){ + if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) { + bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); + if(agc) { + UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; + } + } + } + } + /* Apply gain setting. + * If device is in AGC mode it will ignore the setting. */ try { return rx_gain_group(chan)->set_value(gain, name); } catch (uhd::key_error &) { @@ -828,6 +858,32 @@ public: } } + void set_normalized_rx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_rx_gain(abs_gain, ALL_GAINS, chan); + } + + void set_rx_agc(bool enable, size_t chan = 0) + { + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").set(enable); + } else { + UHD_MSG(warning) << "AGC is not available on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_agc(enable, c); + } + + } + double get_rx_gain(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_value(name); @@ -836,6 +892,21 @@ public: } } + double get_normalized_rx_gain(size_t chan) + { + gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_rx_gain_range(const std::string &name, size_t chan){ try { return rx_gain_group(chan)->get_range(name); @@ -888,6 +959,9 @@ public: if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) { _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); + } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) { + /*For B2xx devices the dc-offset correction is implemented in the rf front-end*/ + _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable").set(enb); } else { UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl; } @@ -912,6 +986,20 @@ public: } } + void set_rx_iq_balance(const bool enb, size_t chan){ + if (chan != ALL_CHANS){ + if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) { + _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable").set(enb); + } else { + UHD_MSG(warning) << "Setting IQ imbalance compensation is not possible on this device." << std::endl; + } + return; + } + for (size_t c = 0; c < get_rx_num_channels(); c++){ + this->set_rx_iq_balance(enb, c); + } + } + void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){ if (chan != ALL_CHANS){ if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) { @@ -926,6 +1014,87 @@ public: } } + std::vector<std::string> get_filter_names(const std::string &search_mask) + { + std::vector<std::string> ret; + + for (size_t chan = 0; chan < get_rx_num_channels(); chan++){ + + if (_tree->exists(rx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or boost::contains(name, search_mask)) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = rx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + for (size_t chan = 0; chan < get_tx_num_channels(); chan++){ + + if (_tree->exists(tx_rf_fe_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_rf_fe_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + if (_tree->exists(rx_dsp_root(chan) / "filters")) { + std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters"); + for(size_t i = 0; i < names.size(); i++) + { + std::string name = tx_dsp_root(chan) / "filters" / names[i]; + if((search_mask.empty()) or (boost::contains(name, search_mask))) { + ret.push_back(name); + } + } + } + + } + + return ret; + } + + filter_info_base::sptr get_filter(const std::string &path) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to get non-existing filter: "+path); + } + + return _tree->access<filter_info_base::sptr>(path / "value").get(); + } + + void set_filter(const std::string &path, filter_info_base::sptr filter) + { + std::vector<std::string> possible_names = get_filter_names(""); + std::vector<std::string>::iterator it; + it = find(possible_names.begin(), possible_names.end(), path); + if (it == possible_names.end()) { + throw uhd::runtime_error("Attempting to set non-existing filter: "+path); + } + + _tree->access<filter_info_base::sptr>(path / "value").set(filter); + } + /******************************************************************* * TX methods ******************************************************************/ @@ -1001,7 +1170,7 @@ public: _tree->subtree(tx_dsp_root(chan)), _tree->subtree(tx_rf_fe_root(chan)), tune_request); - do_tune_freq_results_message(tune_request, result, get_tx_freq(chan), "TX"); + //do_tune_freq_results_message(tune_request, result, get_tx_freq(chan), "TX"); return result; } @@ -1029,6 +1198,17 @@ public: } } + void set_normalized_tx_gain(double gain, size_t chan = 0) + { + if (gain > 1.0 || gain < 0.0) { + throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); + } + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); + set_tx_gain(abs_gain, ALL_GAINS, chan); + } + + double get_tx_gain(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_value(name); @@ -1037,6 +1217,21 @@ public: } } + double get_normalized_tx_gain(size_t chan) + { + gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); + double gain_range_width = gain_range.stop() - gain_range.start(); + // In case we have a device without a range of gains: + if (gain_range_width == 0.0) { + return 0.0; + } + double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; + // Avoid rounding errors: + if (norm_gain > 1.0) return 1.0; + if (norm_gain < 0.0) return 0.0; + return norm_gain; + } + gain_range_t get_tx_gain_range(const std::string &name, size_t chan){ try { return tx_gain_group(chan)->get_range(name); @@ -1181,6 +1376,121 @@ public: return 0; } + void write_register(const std::string &path, const boost::uint32_t field, const boost::uint64_t value, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + if (not reg.is_writable()) { + throw uhd::runtime_error("multi_usrp::write_register - register not writable: " + path); + } + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).write(field, static_cast<boost::uint16_t>(value)); + else + uhd::soft_register_base::cast<uhd::soft_reg16_wo_t>(reg).write(field, static_cast<boost::uint16_t>(value)); + break; + + case 32: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).write(field, static_cast<boost::uint32_t>(value)); + else + uhd::soft_register_base::cast<uhd::soft_reg32_wo_t>(reg).write(field, static_cast<boost::uint32_t>(value)); + break; + + case 64: + if (reg.is_readable()) + uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).write(field, value); + else + uhd::soft_register_base::cast<uhd::soft_reg64_wo_t>(reg).write(field, value); + break; + + default: + throw uhd::assertion_error("multi_usrp::write_register - register has invalid bitwidth"); + } + + } else { + throw uhd::not_implemented_error("multi_usrp::write_register - register IO not supported for this device"); + } + } + + boost::uint64_t read_register(const std::string &path, const boost::uint32_t field, const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + if (not reg.is_readable()) { + throw uhd::runtime_error("multi_usrp::read_register - register not readable: " + path); + } + + switch (reg.get_bitwidth()) { + case 16: + if (reg.is_writable()) + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_rw_t>(reg).read(field)); + else + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg16_ro_t>(reg).read(field)); + break; + + case 32: + if (reg.is_writable()) + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_rw_t>(reg).read(field)); + else + return static_cast<boost::uint64_t>(uhd::soft_register_base::cast<uhd::soft_reg32_ro_t>(reg).read(field)); + break; + + case 64: + if (reg.is_writable()) + return uhd::soft_register_base::cast<uhd::soft_reg64_rw_t>(reg).read(field); + else + return uhd::soft_register_base::cast<uhd::soft_reg64_ro_t>(reg).read(field); + break; + + default: + throw uhd::assertion_error("multi_usrp::read_register - register has invalid bitwidth: " + path); + } + } else { + throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); + } + } + + std::vector<std::string> enumerate_registers(const size_t mboard) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + return accessor->enumerate(); + } else { + return std::vector<std::string>(); + } + } + + register_info_t get_register_info(const std::string &path, const size_t mboard = 0) + { + if (_tree->exists(mb_root(mboard) / "registers")) + { + uhd::soft_regmap_accessor_t::sptr accessor = + _tree->access<uhd::soft_regmap_accessor_t::sptr>(mb_root(mboard) / "registers").get(); + uhd::soft_register_base& reg = accessor->lookup(path); + + register_info_t info; + info.bitwidth = reg.get_bitwidth(); + info.readable = reg.is_readable(); + info.writable = reg.is_writable(); + return info; + } else { + throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device"); + } + } + private: device::sptr _dev; property_tree::sptr _tree; diff --git a/host/lib/usrp/subdev_spec_c.cpp b/host/lib/usrp/subdev_spec_c.cpp new file mode 100644 index 000000000..2c9c20506 --- /dev/null +++ b/host/lib/usrp/subdev_spec_c.cpp @@ -0,0 +1,149 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/subdev_spec.h> + +#include <string.h> + +uhd_error uhd_subdev_spec_pair_free( + uhd_subdev_spec_pair_t *subdev_spec_pair +){ + UHD_SAFE_C( + if(subdev_spec_pair->db_name){ + free(subdev_spec_pair->db_name); + subdev_spec_pair->db_name = NULL; + } + if(subdev_spec_pair->sd_name){ + free(subdev_spec_pair->sd_name); + subdev_spec_pair->sd_name = NULL; + } + ) +} + +uhd_error uhd_subdev_spec_pairs_equal( + const uhd_subdev_spec_pair_t* first, + const uhd_subdev_spec_pair_t* second, + bool *result_out +){ + UHD_SAFE_C( + *result_out = (uhd_subdev_spec_pair_c_to_cpp(first) == + uhd_subdev_spec_pair_c_to_cpp(second)); + ) +} + +uhd_error uhd_subdev_spec_make( + uhd_subdev_spec_handle* h, + const char* markup +){ + UHD_SAFE_C( + (*h) = new uhd_subdev_spec_t; + std::string markup_cpp(markup); + if(!markup_cpp.empty()){ + (*h)->subdev_spec_cpp = uhd::usrp::subdev_spec_t(markup_cpp); + } + ) +} + +uhd_error uhd_subdev_spec_free( + uhd_subdev_spec_handle* h +){ + UHD_SAFE_C( + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_subdev_spec_size( + uhd_subdev_spec_handle h, + size_t *size_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *size_out = h->subdev_spec_cpp.size(); + ) +} + +uhd_error uhd_subdev_spec_push_back( + uhd_subdev_spec_handle h, + const char* markup +){ + UHD_SAFE_C_SAVE_ERROR(h, + h->subdev_spec_cpp.push_back(uhd::usrp::subdev_spec_pair_t(markup)); + ) +} + +uhd_error uhd_subdev_spec_at( + uhd_subdev_spec_handle h, + size_t num, + uhd_subdev_spec_pair_t *subdev_spec_pair_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd_subdev_spec_pair_cpp_to_c( + h->subdev_spec_cpp.at(num), + subdev_spec_pair_out + ); + ) +} + +uhd_error uhd_subdev_spec_to_pp_string( + uhd_subdev_spec_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string pp_string_cpp = h->subdev_spec_cpp.to_pp_string(); + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, pp_string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_subdev_spec_to_string( + uhd_subdev_spec_handle h, + char* string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string string_cpp = h->subdev_spec_cpp.to_string(); + memset(string_out, '\0', strbuffer_len); + strncpy(string_out, string_cpp.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_subdev_spec_last_error( + uhd_subdev_spec_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +uhd::usrp::subdev_spec_pair_t uhd_subdev_spec_pair_c_to_cpp( + const uhd_subdev_spec_pair_t *subdev_spec_pair_c +){ + return uhd::usrp::subdev_spec_pair_t(subdev_spec_pair_c->db_name, + subdev_spec_pair_c->sd_name); +} + +void uhd_subdev_spec_pair_cpp_to_c( + const uhd::usrp::subdev_spec_pair_t &subdev_spec_pair_cpp, + uhd_subdev_spec_pair_t *subdev_spec_pair_c +){ + subdev_spec_pair_c->db_name = strdup(subdev_spec_pair_cpp.db_name.c_str()); + subdev_spec_pair_c->sd_name = strdup(subdev_spec_pair_cpp.sd_name.c_str()); +} diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 70bebe502..47344e841 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the USRP1 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) +LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) IF(ENABLE_USRP1) LIBUHD_APPEND_SOURCES( diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index c6257c7fe..d9894adaf 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2012,2014 Ettus Research LLC +# Copyright 2011-2012,2014-2015 Ettus Research LLC # # 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 @@ -22,21 +22,9 @@ ######################################################################## # Conditionally configure the USRP2 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_USRP2) - ######################################################################## - # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp - ######################################################################## - FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH) - STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH}) - - SET_SOURCE_FILES_PROPERTIES( - ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp - PROPERTIES COMPILE_DEFINITIONS - "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\"" - ) - LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp @@ -45,5 +33,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp new file mode 100644 index 000000000..29bec8b4a --- /dev/null +++ b/host/lib/usrp/usrp2/n200_image_loader.cpp @@ -0,0 +1,646 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <cstring> +#include <iostream> +#include <fstream> + +#include <boost/asio/ip/address_v4.hpp> +#include <boost/assign.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/algorithm/string/erase.hpp> + +#include <uhd/config.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp> + +#include "fw_common.h" +#include "usrp2_iface.hpp" +#include "usrp2_impl.hpp" + +typedef boost::asio::ip::address_v4 ip_v4; + +namespace fs = boost::filesystem; +using namespace boost::algorithm; + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/* + * Constants + */ + +#define N200_FLASH_DATA_PACKET_SIZE 256 +#define N200_UDP_FW_UPDATE_PORT 49154 +#define UDP_TIMEOUT 0.5 + +#define N200_FW_MAX_SIZE_BYTES 31744 +#define N200_PROD_FW_IMAGE_ADDR 0x00300000 +#define N200_SAFE_FW_IMAGE_ADDR 0x003F0000 + +#define N200_FPGA_MAX_SIZE_BYTES 1572864 +#define N200_PROD_FPGA_IMAGE_ADDR 0x00180000 +#define N200_SAFE_FPGA_IMAGE_ADDR 0x00000000 + +/* + * Packet codes + */ +typedef enum { + UNKNOWN = ' ', + + N200_QUERY = 'a', + N200_ACK = 'A', + + GET_FLASH_INFO_CMD = 'f', + GET_FLASH_INFO_ACK = 'F', + + ERASE_FLASH_CMD = 'e', + ERASE_FLASH_ACK = 'E', + + CHECK_ERASING_DONE_CMD = 'd', + DONE_ERASING_ACK = 'D', + NOT_DONE_ERASING_ACK = 'B', + + WRITE_FLASH_CMD = 'w', + WRITE_FLASH_ACK = 'W', + + READ_FLASH_CMD = 'r', + READ_FLASH_ACK = 'R', + + RESET_CMD = 's', + RESET_ACK = 'S', + + GET_HW_REV_CMD = 'v', + GET_HW_REV_ACK = 'V', +} n200_fw_update_id_t; + +/* + * Mapping revision numbers to names + */ +static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of + (0, "n2xx") // Is an N-Series, but the EEPROM value is invalid + (0xa, "n200_r3") + (0x100a, "n200_r4") + (0x10a, "n210_r3") + (0x110a, "n210_r4") +; + +/* + * Packet structure + */ +typedef struct { + boost::uint32_t proto_ver; + boost::uint32_t id; + boost::uint32_t seq; + union { + boost::uint32_t ip_addr; + boost::uint32_t hw_rev; + struct { + boost::uint32_t flash_addr; + boost::uint32_t length; + boost::uint8_t data[256]; + } flash_args; + struct { + boost::uint32_t sector_size_bytes; + boost::uint32_t memory_size_bytes; + } flash_info_args; + } data; +} n200_fw_update_data_t; + +/* + * N-Series burn session + */ +typedef struct { + bool fw; + bool overwrite_safe; + bool reset; + uhd::device_addr_t dev_addr; + std::string burn_type; + std::string filepath; + boost::uint8_t data_in[udp_simple::mtu]; + boost::uint32_t size; + boost::uint32_t max_size; + boost::uint32_t flash_addr; + udp_simple::sptr xport; +} n200_session_t; + +/*********************************************************************** + * uhd::image_loader functionality + **********************************************************************/ + +static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){ + #ifdef UHD_PLATFORM_WIN32 + std::string usrp2_card_burner_gui = "\""; + const std::string nl = " ^\n "; + #else + std::string usrp2_card_burner_gui = "sudo \""; + const std::string nl = " \\\n "; + #endif + + usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py"); + usrp2_card_burner_gui += "\""; + + if(image_loader_args.load_firmware){ + usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"") + % nl + % ((image_loader_args.firmware_path == "") + ? find_image_path("usrp2_fw.bin") + : image_loader_args.firmware_path)); + } + if(image_loader_args.load_fpga){ + usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"") + % nl + % ((image_loader_args.fpga_path == "") + ? find_image_path("usrp2_fpga.bin") + : image_loader_args.fpga_path)); + } + + throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n" + "Instead, plug the device's SD card into your machine and run this command:\n\n" + "%s" + ) % usrp2_card_burner_gui)); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport, + n200_fw_update_id_t pkt_code, + n200_fw_update_data_t *pkt_out, + boost::uint8_t* data){ + pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM); + pkt_out->id = htonx<boost::uint32_t>(pkt_code); + xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); + return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in, + n200_fw_update_id_t pkt_code, + size_t len){ + return (len > offsetof(n200_fw_update_data_t, data) and + ntohl(pkt_in->id) == pkt_code); +} + +static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){ + bool user_specified = image_loader_args.args.has_key("addr") or + image_loader_args.args.has_key("serial") or + image_loader_args.args.has_key("name"); + + uhd::device_addrs_t found = usrp2_find(image_loader_args.args); + + if(found.size() > 0){ + uhd::device_addrs_t n200_found; + udp_simple::sptr rev_xport; + n200_fw_update_data_t pkt_out; + boost::uint8_t data_in[udp_simple::mtu]; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in); + size_t len = 0; + + /* + * Filter out any USRP2 devices by sending a query over the + * UDP update port. Only N-Series devices will respond to + * this query. If the user supplied specific arguments that + * led to a USRP2, throw an error. + */ + BOOST_FOREACH(const uhd::device_addr_t &dev, found){ + rev_xport = udp_simple::make_connected( + dev.get("addr"), + BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT) + ); + + len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in); + if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){ + boost::uint32_t rev = ntohl(pkt_in->data.hw_rev); + std::string hw_rev = n200_filename_map.get(rev, "n2xx"); + + n200_found.push_back(dev); + n200_found[n200_found.size()-1]["hw_rev"] = hw_rev; + } + else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){ + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.") + % ntohl(pkt_in->id))); + } + else if(user_specified){ + // At this point, we haven't received any response, so assume it's a USRP2 + print_usrp2_error(image_loader_args); + } + } + + // At this point, we should have a single N-Series device + if(n200_found.size() == 1){ + return n200_found[0]; + } + else if(n200_found.size() > 1){ + std::string err_msg = "Could not resolve given args to a single N-Series device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, n200_found){ + err_msg += str(boost::format("* %s (addr=%s)\n") + % dev.get("hw_rev") + % dev.get("addr")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + } + + return uhd::device_addr_t(); +} + +/* + * Validate and read firmware image + */ +static void n200_validate_firmware_image(n200_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + session.size = fs::file_size(session.filepath); + session.max_size = N200_FW_MAX_SIZE_BYTES; + + if(session.size > session.max_size){ + throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d") + % session.size % session.max_size)); + } + + // File must have proper header + std::ifstream image_file(session.filepath.c_str(), std::ios::binary); + boost::uint8_t test_bytes[4]; + image_file.seekg(0, std::ios::beg); + image_file.read((char*)test_bytes,4); + image_file.close(); + for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){ + throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.") + % session.filepath)); + } +} + +/* + * Validate and validate FPGA image + */ +static void n200_validate_fpga_image(n200_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + session.size = fs::file_size(session.filepath); + session.max_size = N200_FPGA_MAX_SIZE_BYTES; + + if(session.size > session.max_size){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % session.max_size)); + } + + // File must have proper header + std::ifstream image_file(session.filepath.c_str(), std::ios::binary); + boost::uint8_t test_bytes[63]; + image_file.seekg(0, std::ios::beg); + image_file.read((char*)test_bytes, 63); + bool is_good = false; + for(int i = 0; i < 63; i++){ + if(test_bytes[i] == 255) continue; + else if(test_bytes[i] == 170 and + test_bytes[i+1] == 153){ + is_good = true; + break; + } + } + image_file.close(); + if(not is_good){ + throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.") + % session.filepath)); + } +} + +/* + * Set up a session for burning an N-Series image. This session info + * will be passed into the erase, burn, and verify functions. + */ +static void n200_setup_session(n200_session_t &session, + const image_loader::image_loader_args_t &image_loader_args, + bool fw){ + + + session.fw = fw; + session.reset = image_loader_args.args.has_key("reset"); + + /* + * If no filepath is given, attempt to determine the default image by + * querying the device for its revision. If the device has a corrupt + * EEPROM or is otherwise unable to provide its revision, this is + * impossible, and the user must manually provide a firmware file. + */ + if((session.fw and image_loader_args.firmware_path == "") or + image_loader_args.fpga_path == ""){ + if(session.dev_addr["hw_rev"] == "n2xx"){ + throw uhd::runtime_error("This device's revision cannot be determined. " + "You must manually specify a filepath."); + } + else{ + session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin") + % erase_tail_copy(session.dev_addr["hw_rev"],3))) + : find_image_path(str(boost::format("usrp_%s_fpga.bin") + % session.dev_addr["hw_rev"])); + } + } + else{ + session.filepath = session.fw ? image_loader_args.firmware_path + : image_loader_args.fpga_path; + } + if(session.fw) n200_validate_firmware_image(session); + else n200_validate_fpga_image(session); + + session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe"); + if(session.overwrite_safe){ + session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR + : N200_SAFE_FPGA_IMAGE_ADDR; + session.burn_type = session.fw ? "firmware safe" + : "FPGA safe"; + } + else{ + session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR + : N200_PROD_FPGA_IMAGE_ADDR; + session.burn_type = session.fw ? "firmware" + : "FPGA"; + } + + session.xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)); +} + +static void n200_erase_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + + // Setting up UDP packet + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr); + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size); + + // Begin erasing + size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){ + std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Did not receive response from device."); + } + + // Check for erase completion + while(true){ + len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){ + std::cout << "successful." << std::endl; + break; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + } +} + +static void n200_write_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + size_t len = 0; + + // Write image + std::ifstream image(session.filepath.c_str(), std::ios::binary); + boost::uint32_t current_addr = session.flash_addr; + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); + for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); + memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); + image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE); + + len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){ + std::cout << boost::format("\r-- Writing %s image (%d%%)") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::flush; + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + image.close(); + std::cout << boost::format("\r--Writing %s image..failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){ + image.close(); + std::cout << boost::format("\r--Writing %s image..failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + + current_addr += N200_FLASH_DATA_PACKET_SIZE; + } + std::cout << boost::format("\r-- Writing %s image...successful.") + % session.burn_type + << std::endl; + + image.close(); +} + +static void n200_verify_image(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + size_t len = 0; + + // Read and verify image + std::ifstream image(session.filepath.c_str(), std::ios::binary); + boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE]; + boost::uint32_t current_addr = session.flash_addr; + pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); + boost::uint16_t cmp_len = 0; + for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ + memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE); + memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); + + pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); + image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE); + cmp_len = image.gcount(); + + len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in); + if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){ + std::cout << boost::format("\r-- Verifying %s image (%d%%)") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::flush; + + if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){ + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Failed to verify %s image.") + % session.burn_type)); + } + } + else if(len < offsetof(n200_fw_update_data_t, data)){ + image.close(); + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if(ntohl(pkt_in->id) != READ_FLASH_ACK){ + image.close(); + std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") + % session.burn_type + % int((double(current_addr-session.flash_addr)/double(session.size))*100) + << std::endl; + throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") + % ntohl(pkt_in->id))); + } + + current_addr += N200_FLASH_DATA_PACKET_SIZE; + } + std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type + << std::endl; + + image.close(); +} + +static void n200_reset(n200_session_t &session){ + + // UDP receive buffer + n200_fw_update_data_t pkt_out; + + // There should be no response + std::cout << "-- Resetting device..." << std::flush; + size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in); + if(len > 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset N200."); + } + std::cout << "successful." << std::endl; +} + +// n210_r4 -> N210 r4 +static std::string nice_name(const std::string &fw_rev){ + std::string ret = fw_rev; + ret[0] = ::toupper(ret[0]); + + size_t pos = 0; + if((pos = fw_rev.find("_")) != std::string::npos){ + ret[pos] = ' '; + } + + return ret; +} + +static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + if(!image_loader_args.load_firmware and !image_loader_args.load_fpga){ + return false; + } + + // See if any N2x0 with the given args is found + // This will throw if specific args lead to a USRP2 + n200_session_t session; + session.dev_addr = n200_find(image_loader_args); + if(session.dev_addr.size() == 0){ + return false; + } + + std::cout << boost::format("Unit: USRP %s (%s, %s)") + % nice_name(session.dev_addr.get("hw_rev")) + % session.dev_addr.get("serial") + % session.dev_addr.get("addr") + << std::endl; + + if(image_loader_args.load_firmware){ + n200_setup_session(session, + image_loader_args, + true + ); + + std::cout << "Firmware image: " << session.filepath << std::endl; + + n200_erase_image(session); + n200_write_image(session); + n200_verify_image(session); + if(session.reset and !image_loader_args.load_fpga){ + n200_reset(session); + } + } + if(image_loader_args.load_fpga){ + n200_setup_session(session, + image_loader_args, + false + ); + + std::cout << "FPGA image: " << session.filepath << std::endl; + + n200_erase_image(session); + n200_write_image(session); + n200_verify_image(session); + if(session.reset){ + n200_reset(session); + } + } + + return true; +} + +UHD_STATIC_BLOCK(register_n200_image_loader){ + std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n" + "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n" + "for details on restoring your device."; + + image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 1d41173f8..2b382ae38 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -387,15 +387,15 @@ public: //create the burner commands if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ - const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string(); - const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); + const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py"); + const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd); } else{ const std::string addr = _ctrl_transport->get_recv_addr(); - const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string(); - const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr); - return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd); + const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr); + return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd); } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 1acc1dad3..7b59dfaf1 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32; /*********************************************************************** * Discovery over the udp transport **********************************************************************/ -static device_addrs_t usrp2_find(const device_addr_t &hint_){ +device_addrs_t usrp2_find(const device_addr_t &hint_){ //handle the multi-device discovery device_addrs_t hints = separate_device_addr(hint_); if (hints.size() > 1){ @@ -782,9 +782,6 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) : UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; _tree->access<std::string>(root / "time_source/value").set("gpsdo"); _tree->access<std::string>(root / "clock_source/value").set("gpsdo"); - UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; - const time_t tp = time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1); - _tree->access<time_spec_t>(root / "time" / "pps").set(time_spec_t(tp)); } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 701403029..07cd98b4c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -42,6 +42,7 @@ #include <uhd/transport/vrt_if_packet.hpp> #include <uhd/transport/udp_simple.hpp> #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/types/device_addr.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <uhd/usrp/subdev_spec.hpp> #include <boost/weak_ptr.hpp> @@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; static const std::string USRP2_EEPROM_MAP_KEY = "N100"; +uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_); + //! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( uhd::timed_wb_iface::sptr wb_iface, diff --git a/host/lib/usrp/usrp_c.cpp b/host/lib/usrp/usrp_c.cpp new file mode 100644 index 000000000..69f2bd5e5 --- /dev/null +++ b/host/lib/usrp/usrp_c.cpp @@ -0,0 +1,1413 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* C-Interface for multi_usrp */ + +#include <uhd/utils/static.hpp> +#include <uhd/usrp/multi_usrp.hpp> + +#include <uhd/error.h> +#include <uhd/usrp/usrp.h> + +#include <boost/foreach.hpp> +#include <boost/thread/mutex.hpp> + +#include <string.h> +#include <map> + +/**************************************************************************** + * Helpers + ***************************************************************************/ +uhd::stream_args_t stream_args_c_to_cpp(const uhd_stream_args_t *stream_args_c) +{ + std::string otw_format(stream_args_c->otw_format); + std::string cpu_format(stream_args_c->cpu_format); + std::string args(stream_args_c->args); + std::vector<size_t> channels(stream_args_c->channel_list, stream_args_c->channel_list + stream_args_c->n_channels); + + uhd::stream_args_t stream_args_cpp(cpu_format, otw_format); + stream_args_cpp.args = args; + stream_args_cpp.channels = channels; + + return stream_args_cpp; +} + +uhd::stream_cmd_t stream_cmd_c_to_cpp(const uhd_stream_cmd_t *stream_cmd_c) +{ + uhd::stream_cmd_t stream_cmd_cpp(uhd::stream_cmd_t::stream_mode_t(stream_cmd_c->stream_mode)); + stream_cmd_cpp.num_samps = stream_cmd_c->num_samps; + stream_cmd_cpp.stream_now = stream_cmd_c->stream_now; + stream_cmd_cpp.time_spec = uhd::time_spec_t(stream_cmd_c->time_spec_full_secs, stream_cmd_c->time_spec_frac_secs); + return stream_cmd_cpp; +} + +/**************************************************************************** + * Registry / Pointer Management + ***************************************************************************/ +/* Public structs */ +struct uhd_usrp { + size_t usrp_index; + std::string last_error; +}; + +struct uhd_tx_streamer { + size_t usrp_index; + size_t streamer_index; + std::string last_error; +}; + +struct uhd_rx_streamer { + size_t usrp_index; + size_t streamer_index; + std::string last_error; +}; + +/* Not public: We use this for our internal registry */ +struct usrp_ptr { + uhd::usrp::multi_usrp::sptr ptr; + std::vector< uhd::rx_streamer::sptr > rx_streamers; + std::vector< uhd::tx_streamer::sptr > tx_streamers; + static size_t usrp_counter; +}; +size_t usrp_ptr::usrp_counter = 0; +typedef struct usrp_ptr usrp_ptr; +/* Prefer map, because the list can be discontiguous */ +typedef std::map<size_t, usrp_ptr> usrp_ptrs; + +UHD_SINGLETON_FCN(usrp_ptrs, get_usrp_ptrs); +/* Shortcut for accessing the underlying USRP sptr from a uhd_usrp_handle* */ +#define USRP(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].ptr) +#define RX_STREAMER(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].rx_streamers[h_ptr->streamer_index]) +#define TX_STREAMER(h_ptr) (get_usrp_ptrs()[h_ptr->usrp_index].tx_streamers[h_ptr->streamer_index]) + +/**************************************************************************** + * RX Streamer + ***************************************************************************/ +static boost::mutex _rx_streamer_make_mutex; +uhd_error uhd_rx_streamer_make(uhd_rx_streamer_handle* h){ + UHD_SAFE_C( + boost::mutex::scoped_lock(_rx_streamer_make_mutex); + (*h) = new uhd_rx_streamer; + ) +} + +static boost::mutex _rx_streamer_free_mutex; +uhd_error uhd_rx_streamer_free(uhd_rx_streamer_handle* h){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_rx_streamer_free_mutex); + delete (*h); + (*h) = NULL; + ) +} + +uhd_error uhd_rx_streamer_num_channels(uhd_rx_streamer_handle h, + size_t *num_channels_out){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = RX_STREAMER(h)->get_num_channels(); + ) +} + +uhd_error uhd_rx_streamer_max_num_samps(uhd_rx_streamer_handle h, + size_t *max_num_samps_out){ + UHD_SAFE_C_SAVE_ERROR(h, + *max_num_samps_out = RX_STREAMER(h)->get_max_num_samps(); + ) +} + +uhd_error uhd_rx_streamer_recv( + uhd_rx_streamer_handle h, + void **buffs, + size_t samps_per_buff, + uhd_rx_metadata_handle *md, + double timeout, + bool one_packet, + size_t *items_recvd +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::rx_streamer::buffs_type buffs_cpp(buffs, RX_STREAMER(h)->get_num_channels()); + *items_recvd = RX_STREAMER(h)->recv(buffs_cpp, samps_per_buff, (*md)->rx_metadata_cpp, timeout, one_packet); + ) +} + +uhd_error uhd_rx_streamer_issue_stream_cmd( + uhd_rx_streamer_handle h, + const uhd_stream_cmd_t *stream_cmd +){ + UHD_SAFE_C_SAVE_ERROR(h, + RX_STREAMER(h)->issue_stream_cmd(stream_cmd_c_to_cpp(stream_cmd)); + ) +} + +uhd_error uhd_rx_streamer_last_error( + uhd_rx_streamer_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/**************************************************************************** + * TX Streamer + ***************************************************************************/ +static boost::mutex _tx_streamer_make_mutex; +uhd_error uhd_tx_streamer_make( + uhd_tx_streamer_handle* h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_tx_streamer_make_mutex); + (*h) = new uhd_tx_streamer; + ) +} + +static boost::mutex _tx_streamer_free_mutex; +uhd_error uhd_tx_streamer_free( + uhd_tx_streamer_handle* h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_tx_streamer_free_mutex); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_tx_streamer_num_channels( + uhd_tx_streamer_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = TX_STREAMER(h)->get_num_channels(); + ) +} + +uhd_error uhd_tx_streamer_max_num_samps( + uhd_tx_streamer_handle h, + size_t *max_num_samps_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *max_num_samps_out = TX_STREAMER(h)->get_max_num_samps(); + ) +} + +uhd_error uhd_tx_streamer_send( + uhd_tx_streamer_handle h, + const void **buffs, + size_t samps_per_buff, + uhd_tx_metadata_handle *md, + double timeout, + size_t *items_sent +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tx_streamer::buffs_type buffs_cpp(buffs, TX_STREAMER(h)->get_num_channels()); + *items_sent = TX_STREAMER(h)->send( + buffs_cpp, + samps_per_buff, + (*md)->tx_metadata_cpp, + timeout + ); + ) +} + +uhd_error uhd_tx_streamer_recv_async_msg( + uhd_tx_streamer_handle h, + uhd_async_metadata_handle *md, + const double timeout, + bool *valid +){ + UHD_SAFE_C_SAVE_ERROR(h, + *valid = TX_STREAMER(h)->recv_async_msg((*md)->async_metadata_cpp, timeout); + ) +} + +uhd_error uhd_tx_streamer_last_error( + uhd_tx_streamer_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +/**************************************************************************** + * Generate / Destroy API calls + ***************************************************************************/ +static boost::mutex _usrp_find_mutex; +uhd_error uhd_usrp_find( + const char* args, + uhd_string_vector_handle *strings_out +){ + UHD_SAFE_C( + boost::mutex::scoped_lock _lock(_usrp_find_mutex); + + uhd::device_addrs_t devs = uhd::device::find(std::string(args), uhd::device::USRP); + (*strings_out)->string_vector_cpp.clear(); + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + (*strings_out)->string_vector_cpp.push_back(dev.to_string()); + } + ) +} + +static boost::mutex _usrp_make_mutex; +uhd_error uhd_usrp_make( + uhd_usrp_handle *h, + const char *args +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_make_mutex); + + size_t usrp_count = usrp_ptr::usrp_counter; + usrp_ptr::usrp_counter++; + + // Initialize USRP + uhd::device_addr_t device_addr(args); + usrp_ptr P; + P.ptr = uhd::usrp::multi_usrp::make(device_addr); + + // Dump into registry + get_usrp_ptrs()[usrp_count] = P; + + // Update handle + (*h) = new uhd_usrp; + (*h)->usrp_index = usrp_count; + ) +} + +static boost::mutex _usrp_free_mutex; +uhd_error uhd_usrp_free( + uhd_usrp_handle *h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_free_mutex); + + if(!get_usrp_ptrs().count((*h)->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + get_usrp_ptrs().erase((*h)->usrp_index); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_usrp_last_error( + uhd_usrp_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +static boost::mutex _usrp_get_rx_stream_mutex; +uhd_error uhd_usrp_get_rx_stream( + uhd_usrp_handle h_u, + uhd_stream_args_t *stream_args, + uhd_rx_streamer_handle h_s +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_get_rx_stream_mutex); + + if(!get_usrp_ptrs().count(h_u->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + usrp_ptr &usrp = get_usrp_ptrs()[h_u->usrp_index]; + usrp.rx_streamers.push_back( + usrp.ptr->get_rx_stream(stream_args_c_to_cpp(stream_args)) + ); + h_s->usrp_index = h_u->usrp_index; + h_s->streamer_index = usrp.rx_streamers.size() - 1; + ) +} + +static boost::mutex _usrp_get_tx_stream_mutex; +uhd_error uhd_usrp_get_tx_stream( + uhd_usrp_handle h_u, + uhd_stream_args_t *stream_args, + uhd_tx_streamer_handle h_s +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_get_tx_stream_mutex); + + if(!get_usrp_ptrs().count(h_u->usrp_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + usrp_ptr &usrp = get_usrp_ptrs()[h_u->usrp_index]; + usrp.tx_streamers.push_back( + usrp.ptr->get_tx_stream(stream_args_c_to_cpp(stream_args)) + ); + h_s->usrp_index = h_u->usrp_index; + h_s->streamer_index = usrp.tx_streamers.size() - 1; + ) +} + +/**************************************************************************** + * multi_usrp API calls + ***************************************************************************/ + +#define COPY_INFO_FIELD(out, dict, field) \ + out->field = strdup(dict.get(BOOST_STRINGIZE(field)).c_str()) + +uhd_error uhd_usrp_get_rx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_rx_info_t *info_out +) { + UHD_SAFE_C_SAVE_ERROR(h, + uhd::dict<std::string, std::string> rx_info = USRP(h)->get_usrp_rx_info(chan); + + COPY_INFO_FIELD(info_out, rx_info, mboard_id); + COPY_INFO_FIELD(info_out, rx_info, mboard_serial); + COPY_INFO_FIELD(info_out, rx_info, rx_id); + COPY_INFO_FIELD(info_out, rx_info, rx_subdev_name); + COPY_INFO_FIELD(info_out, rx_info, rx_subdev_spec); + COPY_INFO_FIELD(info_out, rx_info, rx_serial); + COPY_INFO_FIELD(info_out, rx_info, rx_antenna); + ) +} + +uhd_error uhd_usrp_get_tx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_tx_info_t *info_out +) { + UHD_SAFE_C_SAVE_ERROR(h, + uhd::dict<std::string, std::string> tx_info = USRP(h)->get_usrp_tx_info(chan); + + COPY_INFO_FIELD(info_out, tx_info, mboard_id); + COPY_INFO_FIELD(info_out, tx_info, mboard_serial); + COPY_INFO_FIELD(info_out, tx_info, tx_id); + COPY_INFO_FIELD(info_out, tx_info, tx_subdev_name); + COPY_INFO_FIELD(info_out, tx_info, tx_subdev_spec); + COPY_INFO_FIELD(info_out, tx_info, tx_serial); + COPY_INFO_FIELD(info_out, tx_info, tx_antenna); + ) +} + +/**************************************************************************** + * Motherboard methods + ***************************************************************************/ +uhd_error uhd_usrp_set_master_clock_rate( + uhd_usrp_handle h, + double rate, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_master_clock_rate(rate, mboard); + ) +} + +uhd_error uhd_usrp_get_master_clock_rate( + uhd_usrp_handle h, + size_t mboard, + double *clock_rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *clock_rate_out = USRP(h)->get_master_clock_rate(mboard); + ) +} + +uhd_error uhd_usrp_get_pp_string( + uhd_usrp_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(pp_string_out, USRP(h)->get_pp_string().c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_mboard_name( + uhd_usrp_handle h, + size_t mboard, + char* mboard_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(mboard_name_out, USRP(h)->get_mboard_name(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_time_now( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = USRP(h)->get_time_now(mboard); + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_usrp_get_time_last_pps( + uhd_usrp_handle h, + size_t mboard, + time_t *full_secs_out, + double *frac_secs_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp = USRP(h)->get_time_last_pps(mboard); + *full_secs_out = time_spec_cpp.get_full_secs(); + *frac_secs_out = time_spec_cpp.get_frac_secs(); + ) +} + +uhd_error uhd_usrp_set_time_now( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_now(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_set_time_next_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_next_pps(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_set_time_unknown_pps( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_time_unknown_pps(time_spec_cpp); + ) +} + +uhd_error uhd_usrp_get_time_synchronized( + uhd_usrp_handle h, + bool *result_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *result_out = USRP(h)->get_time_synchronized(); + return UHD_ERROR_NONE; + ) +} + +uhd_error uhd_usrp_set_command_time( + uhd_usrp_handle h, + time_t full_secs, + double frac_secs, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::time_spec_t time_spec_cpp(full_secs, frac_secs); + USRP(h)->set_command_time(time_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_clear_command_time( + uhd_usrp_handle h, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->clear_command_time(mboard); + ) +} + +uhd_error uhd_usrp_set_time_source( + uhd_usrp_handle h, + const char* time_source, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_time_source(std::string(time_source), mboard); + ) +} + +uhd_error uhd_usrp_get_time_source( + uhd_usrp_handle h, + size_t mboard, + char* time_source_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(time_source_out, USRP(h)->get_time_source(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_time_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *time_sources_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*time_sources_out)->string_vector_cpp = USRP(h)->get_time_sources(mboard); + ) +} + +uhd_error uhd_usrp_set_clock_source( + uhd_usrp_handle h, + const char* clock_source, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_clock_source(std::string(clock_source), mboard); + ) +} + +uhd_error uhd_usrp_get_clock_source( + uhd_usrp_handle h, + size_t mboard, + char* clock_source_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + strncpy(clock_source_out, USRP(h)->get_clock_source(mboard).c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_clock_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *clock_sources_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*clock_sources_out)->string_vector_cpp = USRP(h)->get_clock_sources(mboard); + ) +} + +uhd_error uhd_usrp_set_clock_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_clock_source_out(enb, mboard); + ) +} + +uhd_error uhd_usrp_get_num_mboards( + uhd_usrp_handle h, + size_t *num_mboards_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_mboards_out = USRP(h)->get_num_mboards(); + ) +} + +uhd_error uhd_usrp_get_mboard_sensor( + uhd_usrp_handle h, + const char* name, + size_t mboard, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_mboard_sensor(name, mboard)); + ) +} + +uhd_error uhd_usrp_get_mboard_sensor_names( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *mboard_sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*mboard_sensor_names_out)->string_vector_cpp = USRP(h)->get_mboard_sensor_names(mboard); + ) +} + +uhd_error uhd_usrp_set_user_register( + uhd_usrp_handle h, + uint8_t addr, + uint32_t data, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_user_register(addr, data, mboard); + ) +} + +/**************************************************************************** + * EEPROM access methods + ***************************************************************************/ + +uhd_error uhd_usrp_get_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/eeprom") + % mboard); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + mb_eeprom->mboard_eeprom_cpp = ptree->access<uhd::usrp::mboard_eeprom_t>(eeprom_path).get(); + ) +} + +uhd_error uhd_usrp_set_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/eeprom") + % mboard); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + ptree->access<uhd::usrp::mboard_eeprom_t>(eeprom_path).set(mb_eeprom->mboard_eeprom_cpp); + ) +} + +uhd_error uhd_usrp_get_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/dboards/%s/%s_eeprom") + % mboard % slot % unit); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + db_eeprom->dboard_eeprom_cpp = ptree->access<uhd::usrp::dboard_eeprom_t>(eeprom_path).get(); + ) +} + +uhd_error uhd_usrp_set_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::fs_path eeprom_path = str(boost::format("/mboards/%d/dboards/%s/%s_eeprom") + % mboard % slot % unit); + + uhd::property_tree::sptr ptree = USRP(h)->get_device()->get_tree(); + ptree->access<uhd::usrp::dboard_eeprom_t>(eeprom_path).set(db_eeprom->dboard_eeprom_cpp); + ) +} + +/**************************************************************************** + * RX methods + ***************************************************************************/ + +uhd_error uhd_usrp_set_rx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_subdev_spec(subdev_spec->subdev_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_get_rx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + subdev_spec_out->subdev_spec_cpp = USRP(h)->get_rx_subdev_spec(mboard); + ) +} + +uhd_error uhd_usrp_get_rx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = USRP(h)->get_rx_num_channels(); + ) +} + +uhd_error uhd_usrp_get_rx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* rx_subdev_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string rx_subdev_name = USRP(h)->get_rx_subdev_name(chan); + strncpy(rx_subdev_name_out, rx_subdev_name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_set_rx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_rate(rate, chan); + ) +} + +uhd_error uhd_usrp_get_rx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *rate_out = USRP(h)->get_rx_rate(chan); + ) +} + +uhd_error uhd_usrp_get_rx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + rates_out->meta_range_cpp = USRP(h)->get_rx_rates(chan); + ) +} + +uhd_error uhd_usrp_set_rx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tune_request_t tune_request_cpp = uhd_tune_request_c_to_cpp(tune_request); + uhd::tune_result_t tune_result_cpp = USRP(h)->set_rx_freq(tune_request_cpp, chan); + uhd_tune_result_cpp_to_c(tune_result_cpp, tune_result); + ) +} + +uhd_error uhd_usrp_get_rx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *freq_out = USRP(h)->get_rx_freq(chan); + ) +} + +uhd_error uhd_usrp_get_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_rx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_get_fe_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_fe_rx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_set_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + USRP(h)->set_rx_gain(gain, chan); + } + else{ + USRP(h)->set_rx_gain(gain, name, chan); + } + ) +} + +uhd_error uhd_usrp_set_normalized_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_normalized_rx_gain(gain, chan); + ) +} + +uhd_error uhd_usrp_set_rx_agc( + uhd_usrp_handle h, + bool enable, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_agc(enable, chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + *gain_out = USRP(h)->get_rx_gain(chan); + } + else{ + *gain_out = USRP(h)->get_rx_gain(name, chan); + } + ) +} + +uhd_error uhd_usrp_get_normalized_rx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *gain_out = USRP(h)->get_normalized_rx_gain(chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + gain_range_out->meta_range_cpp = USRP(h)->get_rx_gain_range(name, chan); + ) +} + +uhd_error uhd_usrp_get_rx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gain_names_out)->string_vector_cpp = USRP(h)->get_rx_gain_names(chan); + ) +} + +uhd_error uhd_usrp_set_rx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_antenna(std::string(ant), chan); + ) +} + +uhd_error uhd_usrp_get_rx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string rx_antenna = USRP(h)->get_rx_antenna(chan); + strncpy(ant_out, rx_antenna.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_rx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*antennas_out)->string_vector_cpp = USRP(h)->get_rx_antennas(chan); + ) +} + +uhd_error uhd_usrp_set_rx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_bandwidth(bandwidth, chan); + ) +} + +uhd_error uhd_usrp_get_rx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *bandwidth_out = USRP(h)->get_rx_bandwidth(chan); + ) +} + +uhd_error uhd_usrp_get_rx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + bandwidth_range_out->meta_range_cpp = USRP(h)->get_rx_bandwidth_range(chan); + ) +} + +uhd_error uhd_usrp_get_rx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_rx_sensor(name, chan)); + ) +} + +uhd_error uhd_usrp_get_rx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP(h)->get_rx_sensor_names(chan); + ) +} + +uhd_error uhd_usrp_set_rx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_dc_offset(enb, chan); + ) +} + +uhd_error uhd_usrp_set_rx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_rx_iq_balance(enb, chan); + ) +} + +/**************************************************************************** + * TX methods + ***************************************************************************/ + +uhd_error uhd_usrp_set_tx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_subdev_spec(subdev_spec->subdev_spec_cpp, mboard); + ) +} + +uhd_error uhd_usrp_get_tx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + subdev_spec_out->subdev_spec_cpp = USRP(h)->get_tx_subdev_spec(mboard); + ) +} + + +uhd_error uhd_usrp_get_tx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_channels_out = USRP(h)->get_tx_num_channels(); + ) +} + +uhd_error uhd_usrp_get_tx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* tx_subdev_name_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string tx_subdev_name = USRP(h)->get_tx_subdev_name(chan); + strncpy(tx_subdev_name_out, tx_subdev_name.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_set_tx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_rate(rate, chan); + ) +} + +uhd_error uhd_usrp_get_tx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *rate_out = USRP(h)->get_tx_rate(chan); + ) +} + +uhd_error uhd_usrp_get_tx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + rates_out->meta_range_cpp = USRP(h)->get_tx_rates(chan); + ) +} + +uhd_error uhd_usrp_set_tx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::tune_request_t tune_request_cpp = uhd_tune_request_c_to_cpp(tune_request); + uhd::tune_result_t tune_result_cpp = USRP(h)->set_tx_freq(tune_request_cpp, chan); + uhd_tune_result_cpp_to_c(tune_result_cpp, tune_result); + ) +} + +uhd_error uhd_usrp_get_tx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *freq_out = USRP(h)->get_tx_freq(chan); + ) +} + +uhd_error uhd_usrp_get_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_tx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_get_fe_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + freq_range_out->meta_range_cpp = USRP(h)->get_fe_tx_freq_range(chan); + ) +} + +uhd_error uhd_usrp_set_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + USRP(h)->set_tx_gain(gain, chan); + } + else{ + USRP(h)->set_tx_gain(gain, name, chan); + } + ) +} + +uhd_error uhd_usrp_set_normalized_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_normalized_tx_gain(gain, chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string name(gain_name); + if(name.empty()){ + *gain_out = USRP(h)->get_tx_gain(chan); + } + else{ + *gain_out = USRP(h)->get_tx_gain(name, chan); + } + ) +} + +uhd_error uhd_usrp_get_normalized_tx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *gain_out = USRP(h)->get_normalized_tx_gain(chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + gain_range_out->meta_range_cpp = USRP(h)->get_tx_gain_range(name, chan); + ) +} + +uhd_error uhd_usrp_get_tx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gain_names_out)->string_vector_cpp = USRP(h)->get_tx_gain_names(chan); + ) +} + +uhd_error uhd_usrp_set_tx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_antenna(std::string(ant), chan); + ) +} + +uhd_error uhd_usrp_get_tx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + std::string tx_antenna = USRP(h)->get_tx_antenna(chan); + strncpy(ant_out, tx_antenna.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_get_tx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*antennas_out)->string_vector_cpp = USRP(h)->get_tx_antennas(chan); + ) +} + +uhd_error uhd_usrp_set_tx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_bandwidth(bandwidth, chan); + ) +} + +uhd_error uhd_usrp_get_tx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *bandwidth_out = USRP(h)->get_tx_bandwidth(chan); + ) +} + +uhd_error uhd_usrp_get_tx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + bandwidth_range_out->meta_range_cpp = USRP(h)->get_tx_bandwidth_range(chan); + ) +} + +uhd_error uhd_usrp_get_tx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP(h)->get_tx_sensor(name, chan)); + ) +} + +uhd_error uhd_usrp_get_tx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP(h)->get_tx_sensor_names(chan); + ) +} + +uhd_error uhd_usrp_set_tx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_dc_offset(enb, chan); + ) +} + +uhd_error uhd_usrp_set_tx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_tx_iq_balance(enb, chan); + ) +} + +/**************************************************************************** + * GPIO methods + ***************************************************************************/ + +uhd_error uhd_usrp_get_gpio_banks( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gpio_banks_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*gpio_banks_out)->string_vector_cpp = USRP(h)->get_gpio_banks(chan); + ) +} + +uhd_error uhd_usrp_set_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + uint32_t value, + uint32_t mask, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->set_gpio_attr(std::string(bank), std::string(attr), + value, mask, mboard); + ) +} + +uhd_error uhd_usrp_get_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + size_t mboard, + uint32_t *attr_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *attr_out = USRP(h)->get_gpio_attr(std::string(bank), std::string(attr), mboard); + ) +} + +uhd_error uhd_usrp_enumerate_registers( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *registers_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*registers_out)->string_vector_cpp = USRP(h)->enumerate_registers(mboard); + ) +} + +uhd_error uhd_usrp_get_register_info( + uhd_usrp_handle h, + const char* path, + size_t mboard, + uhd_usrp_register_info_t *register_info_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + uhd::usrp::multi_usrp::register_info_t register_info_cpp = USRP(h)->get_register_info(path, mboard); + register_info_out->bitwidth = register_info_cpp.bitwidth; + register_info_out->readable = register_info_cpp.readable; + register_info_out->writable = register_info_cpp.writable; + ) +} + +uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + uint64_t value, + size_t mboard +){ + UHD_SAFE_C_SAVE_ERROR(h, + USRP(h)->write_register(path, field, value, mboard); + ) +} + +uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + size_t mboard, + uint64_t *value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *value_out = USRP(h)->read_register(path, field, mboard); + ) +} diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index a588f901b..3d6348eec 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013 Ettus Research LLC +# Copyright 2013,2015 Ettus Research LLC # # 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 @@ -22,7 +22,7 @@ ######################################################################## # Conditionally configure the X300 support ######################################################################## -LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_X300) LIBUHD_APPEND_SOURCES( @@ -34,5 +34,8 @@ IF(ENABLE_X300) ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c ) ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c new file mode 100644 index 000000000..1d09cbe22 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.c @@ -0,0 +1,80 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "cdecode.h" + +int base64_decode_value(char value_in){ + static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if ((signed char)value_in < 0 || value_in > decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in){ + state_in->step = step_a; + state_in->plainchar = 0; +} + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){ + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step){ + while (1){ + case step_a: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + + case step_b: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do{ + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do{ + if (codechar == code_in+length_in){ + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while ((signed char)fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h new file mode 100644 index 000000000..b8da55aa1 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.h @@ -0,0 +1,36 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> + +typedef enum{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); + +#ifdef __cplusplus +} +#endif + +#endif /* BASE64_CDECODE_H */ diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp index b0e4e4b95..ce6102b35 100644 --- a/host/lib/usrp/x300/x300_adc_ctrl.cpp +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -55,8 +55,8 @@ public: _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS; _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT; _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; - _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; - _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS4_26; + _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS4_26; this->send_ads62p48_reg(0); diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp new file mode 100644 index 000000000..e08825749 --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -0,0 +1,414 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "x300_impl.hpp" +#include <boost/date_time/posix_time/posix_time_io.hpp> + +using namespace uhd::usrp::x300; + +/*********************************************************************** + * DAC: Reset and synchronization operations + **********************************************************************/ + +void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) +{ + if (radios.size() < 2) return; //Nothing to synchronize + + //**PRECONDITION** + //This function assumes that all the VITA times in "radios" are synchronized + //to a common reference. Currently, this function is called in get_tx_stream + //which also has the same precondition. + + //Reinitialize and resync all DACs + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->dac->reset_and_resync(); + } + + //Get a rough estimate of the cumulative command latency + boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->ctrl->peek64(uhd::usrp::radio::RB64_TIME_NOW); //Discard value. We are just timing the call + } + boost::posix_time::time_duration t_elapsed = + boost::posix_time::microsec_clock::local_time() - t_start; + + //Add 100% of headroom + uncertaintly to the command time + boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; + + //Pick radios[0] as the time reference. + uhd::time_spec_t sync_time = + radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); + + //Send the sync command + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->ctrl->set_time(sync_time); + radios[i]->ctrl->poke32(uhd::usrp::radio::sr_addr(uhd::usrp::radio::DACSYNC), 0x1); //Arm FRAMEP/N sync pulse + radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time + } + + //Wait and check status + boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); + for (size_t i = 0; i < radios.size(); i++) { + radios[i]->dac->verify_sync(); + } +} + +/*********************************************************************** + * ADC: Self-test operations + **********************************************************************/ + +static void check_adc(uhd::wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i) +{ + boost::uint32_t adc_rb = iface->peek32(uhd::usrp::radio::RB32_RX); + adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA + if (val != adc_rb) { + throw uhd::runtime_error( + (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str()); + } +} + +void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) { + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + + //First test basic patterns + perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r); + perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r); + perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r); + perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r); + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("zeros", "custom", 1 << k); + check_adc(perif.ctrl, 1 << (k+2),r); + } + for (size_t k = 0; k < 14; k++) + { + perif.adc->set_test_word("custom", "zeros", 1 << k); + check_adc(perif.ctrl, 1 << (k+18),r); + } + + //Turn on ramp pattern test + perif.adc->set_test_word("ramp", "ramp"); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + } + boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); + + bool passed = true; + std::string status_str; + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + perif.regmap->misc_ins_reg.refresh(); + + std::string i_status, q_status; + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR)) + i_status = "Bit Errors!"; + else + i_status = "Good"; + else + i_status = "Not Locked!"; + + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) + if (perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR)) + q_status = "Bit Errors!"; + else + q_status = "Good"; + else + q_status = "Not Locked!"; + + passed = passed && (i_status == "Good") && (q_status == "Good"); + status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str(); + + //Return to normal mode + perif.adc->set_test_word("normal", "normal"); + } + + if (not passed) { + throw uhd::runtime_error( + (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str()); + } +} + +void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s) +{ + static const size_t SECS_PER_ITER = 5; + UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") + % duration_s % SECS_PER_ITER; + + size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); + size_t num_failures = 0; + for (size_t iter = 0; iter < num_iters; iter++) { + //Print date and time + boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); + std::ostringstream time_strm; + time_strm.imbue(std::locale(std::locale::classic(), facet)); + time_strm << boost::posix_time::second_clock::local_time(); + //Run self-test + UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); + try { + self_test_adcs(mb, SECS_PER_ITER*1000); + UHD_MSG(status) << "passed" << std::endl; + } catch(std::exception &e) { + num_failures++; + UHD_MSG(status) << e.what() << std::endl; + } + + } + if (num_failures == 0) { + UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; + } else { + throw uhd::runtime_error( + (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); + } +} + +/*********************************************************************** + * ADC: Self-calibration operations + **********************************************************************/ + +void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status) +{ + radio_perifs_t& perif = mb.radio_perifs[radio_i]; + if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; + + static const boost::uint32_t NUM_DELAY_STEPS = 32; //The IDELAYE2 element has 32 steps + static const boost::uint32_t NUM_RETRIES = 2; //Retry self-cal if it fails in warmup situations + static const boost::int32_t MIN_WINDOW_LEN = 4; + + boost::int32_t win_start = -1, win_stop = -1; + boost::uint32_t iter = 0; + while (iter++ < NUM_RETRIES) { + for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { + //Apply delay + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + + boost::uint32_t err_code = 0; + + // -- Test I Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + perif.adc->set_test_word("ramp", "ones"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //10ms @ 200MHz = 2 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { + err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + // -- Test Q Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + perif.adc->set_test_word("ones", "ramp"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //10ms @ 200MHz = 2 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + if (perif.regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { + err_code += perif.regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + if (err_code == 0) { + if (win_start == -1) { //This is the first window + win_start = dly_tap; + win_stop = dly_tap; + } else { //We are extending the window + win_stop = dly_tap; + } + } else { + if (win_start != -1) { //A valid window turned invalid + if (win_stop - win_start >= MIN_WINDOW_LEN) { + break; //Valid window found + } else { + win_start = -1; //Reset window + } + } + } + //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); + } + + //Retry the self-cal if it fails + if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { + win_start = -1; + win_stop = -1; + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); + } else { + break; + } + } + perif.adc->set_test_word("normal", "normal"); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + + if (win_start == -1) { + throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); + } + + if (win_stop-win_start < MIN_WINDOW_LEN) { + throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); + } + + boost::uint32_t ideal_tap = (win_stop + win_start) / 2; + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + + if (print_status) { + double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps + UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; + } +} + +double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay) +{ + UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; + + //Effective resolution of the self-cal. + static const size_t NUM_DELAY_STEPS = 100; + + double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns + double delay_start = 0.0; + double delay_range = 2 * master_clk_period; + double delay_incr = delay_range / NUM_DELAY_STEPS; + + UHD_MSG(status) << "Measuring..." << std::flush; + double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); + double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); + + //Iterate through several values of delays and measure ADC data integrity + std::vector< std::pair<double,bool> > results; + for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { + //Delay the ADC clock (will set both Ch0 and Ch1 delays) + double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); + wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1); + + boost::uint32_t err_code = 0; + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + //Test each channel (I and Q) individually so as to not accidentally trigger + //on the data from the other channel if there is a swap + + // -- Test I Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + mb.radio_perifs[r].adc->set_test_word("ramp", "ones"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //50ms @ 200MHz = 10 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) { + err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + + // -- Test Q Channel -- + //Put ADC in ramp test mode. Tie the other channel to all ones. + mb.radio_perifs[r].adc->set_test_word("ones", "ramp"); + //Turn on the pattern checker in the FPGA. It will lock when it sees a zero + //and count deviations from the expected value + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + //50ms @ 200MHz = 10 million samples + boost::this_thread::sleep(boost::posix_time::milliseconds(50)); + if (mb.radio_perifs[r].regmap->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) { + err_code += mb.radio_perifs[r].regmap->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR); + } else { + err_code += 100; //Increment error code by 100 to indicate no lock + } + } + //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); + results.push_back(std::pair<double,bool>(delay, err_code==0)); + } + + //Calculate the valid window + int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; + for (size_t i = 0; i < results.size(); i++) { + std::pair<double,bool>& item = results[i]; + if (item.second) { //If data is stable + if (cur_start_idx == -1) { //This is the first window + cur_start_idx = i; + cur_stop_idx = i; + } else { //We are extending the window + cur_stop_idx = i; + } + } else { + if (cur_start_idx == -1) { //We haven't yet seen valid data + //Do nothing + } else if (win_start_idx == -1) { //We passed the first valid window + win_start_idx = cur_start_idx; + win_stop_idx = cur_stop_idx; + } else { //Update cached window if current window is larger + double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; + double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; + if (cur_win_len > cached_win_len) { + win_start_idx = cur_start_idx; + win_stop_idx = cur_stop_idx; + } + } + //Reset current window + cur_start_idx = -1; + cur_stop_idx = -1; + } + } + if (win_start_idx == -1) { + throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); + } + + double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; + double win_length = results[win_stop_idx].first - results[win_start_idx].first; + if (win_length < master_clk_period/4) { + throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); + } + + //Cycle slip the relative delay by a clock cycle to prevent sample misalignment + //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need + bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); + if (cycle_slip) { + win_center -= master_clk_period; + } + + if (apply_delay) { + UHD_MSG(status) << "Validating..." << std::flush; + //Apply delay + win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center); //Sets ADC0 and ADC1 + wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, 0.1); + //Validate + self_test_adcs(mb, 2000); + } else { + //Restore delay + mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay); //Sets ADC0 and ADC1 + } + + //Teardown + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + mb.radio_perifs[r].adc->set_test_word("normal", "normal"); + mb.radio_perifs[r].regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + } + UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % + (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); + + return win_center; +} diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 6450686dd..d5687f5cc 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -21,6 +21,7 @@ #include <uhd/utils/math.hpp> #include <boost/cstdint.hpp> #include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> #include <stdexcept> #include <cmath> #include <cstdlib> @@ -29,6 +30,30 @@ static const double X300_REF_CLK_OUT_RATE = 10e6; static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045; static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6; +struct x300_clk_delays { + x300_clk_delays() : + fpga_dly_ns(0.0),adc_dly_ns(0.0),dac_dly_ns(0.0),db_rx_dly_ns(0.0),db_tx_dly_ns(0.0) + {} + x300_clk_delays(double fpga, double adc, double dac, double db_rx, double db_tx) : + fpga_dly_ns(fpga),adc_dly_ns(adc),dac_dly_ns(dac),db_rx_dly_ns(db_rx),db_tx_dly_ns(db_tx) + {} + + double fpga_dly_ns; + double adc_dly_ns; + double dac_dly_ns; + double db_rx_dly_ns; + double db_tx_dly_ns; +}; + +// Tune the FPGA->ADC clock delay to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. +// If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC +// because the data interface clock is generated from FPGA_CLK. +static const x300_clk_delays X300_REV0_6_CLK_DELAYS = x300_clk_delays( + /*fpga=*/0.000, /*adc=*/2.200, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); + +static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays( + /*fpga=*/0.000, /*adc=*/0.000, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); + using namespace uhd; x300_clock_ctrl::~x300_clock_ctrl(void){ @@ -213,6 +238,187 @@ public: _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); } + double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) { + //All dividers have are delayed by 5 taps by default. The delay + //set by this function is relative to the 5 tap delay + static const boost::uint16_t DDLY_MIN_TAPS = 5; + static const boost::uint16_t DDLY_MAX_TAPS = 522; //Extended mode + + //The resolution and range of the analog delay is fixed + static const double ADLY_RES_NS = 0.025; + static const double ADLY_MIN_NS = 0.500; + static const double ADLY_MAX_NS = 0.975; + + //Each digital tap delays the clock by one VCO period + double vco_period_ns = 1.0e9/_vco_freq; + double half_vco_period_ns = vco_period_ns/2.0; + + //Implement as much of the requested delay using digital taps. Whatever is leftover + //will be made up using the analog delay element and the half-cycle digital tap. + //A caveat here is that the analog delay starts at ADLY_MIN_NS, so we need to back off + //by that much when coming up with the digital taps so that the difference can be made + //up using the analog delay. + boost::uint16_t ddly_taps = 0; + if (delay_ns < ADLY_MIN_NS) { + ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns)/vco_period_ns)); + } else { + ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns-ADLY_MIN_NS)/vco_period_ns)); + } + double leftover_delay = delay_ns - (vco_period_ns * ddly_taps); + + //Compute settings + boost::uint16_t ddly_value = ddly_taps + DDLY_MIN_TAPS; + bool adly_en = false; + boost::uint8_t adly_value = 0; + boost::uint8_t half_shift_en = 0; + + if (ddly_value > DDLY_MAX_TAPS) { + throw uhd::value_error("set_clock_delay: Requested delay is out of range."); + } + + double coerced_delay = (vco_period_ns * ddly_taps); + if (leftover_delay > ADLY_MAX_NS) { + //The VCO is running too slowly for us to compensate the digital delay difference using + //analog delay. Do the best we can. + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((ADLY_MAX_NS-ADLY_MIN_NS)/ADLY_RES_NS)); + coerced_delay += ADLY_MAX_NS; + } else if (leftover_delay >= ADLY_MIN_NS && leftover_delay <= ADLY_MAX_NS) { + //The leftover delay can be compensated by the analog delay up to the analog delay resolution + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay-ADLY_MIN_NS)/ADLY_RES_NS)); + coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value); + } else if (leftover_delay >= (ADLY_MIN_NS - half_vco_period_ns) && leftover_delay < ADLY_MIN_NS) { + //The leftover delay if less than the minimum supported analog delay but if we move the digital + //delay back by half a VCO cycle then it will be in the range of the analog delay. So do that! + adly_en = true; + adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay+half_vco_period_ns-ADLY_MIN_NS)/ADLY_RES_NS)); + half_shift_en = 1; + coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value)-half_vco_period_ns; + } else { + //Even after moving the digital delay back by half a cycle, we cannot make up the difference + //so give up on compensating for the difference from the digital delay tap. + //If control reaches here then the value of leftover_delay is possible very small and will still + //be close to what the client requested. + } + + UHD_LOGV(often) + << boost::format("x300_clock_ctrl::set_clock_delay: Which=%d, Requested=%f, Digital Taps=%d, Half Shift=%d, Analog Delay=%d (%s), Coerced Delay=%fns" + ) % which % delay_ns % ddly_value % (half_shift_en?"ON":"OFF") % ((int)adly_value) % (adly_en?"ON":"OFF") % coerced_delay << std::endl; + + //Apply settings + switch (which) + { + case X300_CLOCK_WHICH_FPGA: + _lmk04816_regs.CLKout0_1_DDLY = ddly_value; + _lmk04816_regs.CLKout0_1_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout0_1_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_PD; + } + write_regs(0); + write_regs(6); + _delays.fpga_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DB0_RX: + case X300_CLOCK_WHICH_DB1_RX: + _lmk04816_regs.CLKout2_3_DDLY = ddly_value; + _lmk04816_regs.CLKout2_3_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout2_3_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_PD; + } + write_regs(1); + write_regs(6); + _delays.db_rx_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DB0_TX: + case X300_CLOCK_WHICH_DB1_TX: + _lmk04816_regs.CLKout4_5_DDLY = ddly_value; + _lmk04816_regs.CLKout4_5_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout4_5_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_PD; + } + write_regs(2); + write_regs(7); + _delays.db_tx_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_DAC0: + case X300_CLOCK_WHICH_DAC1: + _lmk04816_regs.CLKout6_7_DDLY = ddly_value; + _lmk04816_regs.CLKout6_7_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout6_7_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_PD; + } + write_regs(3); + write_regs(7); + _delays.dac_dly_ns = coerced_delay; + break; + case X300_CLOCK_WHICH_ADC0: + case X300_CLOCK_WHICH_ADC1: + _lmk04816_regs.CLKout8_9_DDLY = ddly_value; + _lmk04816_regs.CLKout8_9_HS = half_shift_en; + if (adly_en) { + _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_BOTH; + _lmk04816_regs.CLKout8_9_ADLY = adly_value; + } else { + _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_PD; + _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_PD; + } + write_regs(4); + write_regs(8); + _delays.adc_dly_ns = coerced_delay; + break; + default: + throw uhd::value_error("set_clock_delay: Requested source is invalid."); + } + + //Delays are applied only on a sync event + if (resync) sync_clocks(); + + return coerced_delay; + } + + double get_clock_delay(const x300_clock_which_t which) { + switch (which) + { + case X300_CLOCK_WHICH_FPGA: + return _delays.fpga_dly_ns; + case X300_CLOCK_WHICH_DB0_RX: + case X300_CLOCK_WHICH_DB1_RX: + return _delays.db_rx_dly_ns; + case X300_CLOCK_WHICH_DB0_TX: + case X300_CLOCK_WHICH_DB1_TX: + return _delays.db_tx_dly_ns; + case X300_CLOCK_WHICH_DAC0: + case X300_CLOCK_WHICH_DAC1: + return _delays.dac_dly_ns; + case X300_CLOCK_WHICH_ADC0: + case X300_CLOCK_WHICH_ADC1: + return _delays.adc_dly_ns; + default: + throw uhd::value_error("get_clock_delay: Requested source is invalid."); + } + } private: @@ -409,7 +615,6 @@ private: _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; this->write_regs(0); _lmk04816_regs.CLKout0_1_DIV = master_clock_div; - _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X; this->write_regs(0); // Register 1 @@ -433,9 +638,6 @@ private: _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX - // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. - // This delay may need to vary due to temperature. Tested and verified at room temperature only. - _lmk04816_regs.CLKout0_1_ADLY = 0x10; // Register 7 _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX @@ -501,6 +703,19 @@ private: // PLL2_P_30 set in individual cases above // PLL2_N_30 set in individual cases above + if (_hw_rev >= 7) { + _delays = X300_REV7_CLK_DELAYS; + } else { + _delays = X300_REV0_6_CLK_DELAYS; + } + + //Apply delay values + set_clock_delay(X300_CLOCK_WHICH_FPGA, _delays.fpga_dly_ns, false); + set_clock_delay(X300_CLOCK_WHICH_DB0_RX, _delays.db_rx_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_DB0_TX, _delays.db_tx_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_ADC0, _delays.adc_dly_ns, false); //Sets both Ch0 and Ch1 + set_clock_delay(X300_CLOCK_WHICH_DAC0, _delays.dac_dly_ns, false); //Sets both Ch0 and Ch1 + /* Write the configuration values into the LMK */ for (size_t i = 1; i <= 16; ++i) { this->write_regs(i); @@ -512,13 +727,14 @@ private: this->sync_clocks(); } - const spi_iface::sptr _spiface; - const size_t _slaveno; - const size_t _hw_rev; - const double _master_clock_rate; - const double _system_ref_rate; - lmk04816_regs_t _lmk04816_regs; - double _vco_freq; + const spi_iface::sptr _spiface; + const size_t _slaveno; + const size_t _hw_rev; + const double _master_clock_rate; + const double _system_ref_rate; + lmk04816_regs_t _lmk04816_regs; + double _vco_freq; + x300_clk_delays _delays; }; x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp index 9c08aa356..160a14e6d 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.hpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -33,7 +33,7 @@ enum x300_clock_which_t X300_CLOCK_WHICH_DB0_TX, X300_CLOCK_WHICH_DB1_RX, X300_CLOCK_WHICH_DB1_TX, - X300_CLOCK_WHICH_TEST, + X300_CLOCK_WHICH_FPGA, }; class x300_clock_ctrl : boost::noncopyable @@ -94,6 +94,22 @@ public: */ virtual void set_ref_out(const bool) = 0; + /*! Set the clock delay for the given clock divider. + * \param which which clock + * \param rate the delay in nanoseconds + * \param resync resync clocks to apply delays + * \return the actual delay value set + * \throw exception when which invalid or delay_ns out of range + */ + virtual double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) = 0; + + /*! Get the clock delay for the given clock divider. + * \param which which clock + * \return the actual delay value set + * \throw exception when which invalid + */ + virtual double get_clock_delay(const x300_clock_which_t which) = 0; + /*! Reset the clocks. * Should be called if the reference clock changes * to reduce the time required to achieve a lock. diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index d3bcb8644..bb41146b6 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -129,12 +129,16 @@ public: _check_pll(); // Configure digital interface settings - write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye - write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface - //fpga wants I,Q in the sample word: - //first transaction goes into low bits - //second transaction goes into high bits - //therefore, we want Q to go first (bit 6 == 1) + // Bypass DCI delay. We center the clock edge in the data + // valid window in the FPGA by phase shifting the DCI going + // to the DAC. + write_ad9146_reg(0x16, 0x04); + // 2's comp, I first, byte wide interface + write_ad9146_reg(0x03, 0x00); + // FPGA wants I,Q in the sample word: + // - First transaction goes into low bits + // - Second transaction goes into high bits + // therefore, we want Q to go first (bit 6 == 1) write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode // Configure interpolation filters diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 76531f921..b0b7cfd02 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -29,10 +29,11 @@ extern "C" { #endif -#define X300_MAX_HW_REV 6 -#define X300_FW_COMPAT_MAJOR 3 +#define X300_REVISION_COMPAT 7 +#define X300_REVISION_MIN 2 +#define X300_FW_COMPAT_MAJOR 4 #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 9 +#define X300_FPGA_COMPAT_MAJOR 14 //shared memory sections - in between the stack and the program space #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp new file mode 100644 index 000000000..9ec8a2e13 --- /dev/null +++ b/host/lib/usrp/x300/x300_image_loader.cpp @@ -0,0 +1,416 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <fstream> +#include <vector> + +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/status.h> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "x300_impl.hpp" +#include "x300_fw_common.h" +#include "cdecode.h" + +namespace fs = boost::filesystem; + +using namespace boost::algorithm; +using namespace uhd; +using namespace uhd::transport; + +/* + * Constants + */ +#define X300_FPGA_BIN_SIZE_BYTES 15877916 +#define X300_FPGA_BIT_SIZE_BYTES 15878032 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_FLASH_SECTOR_SIZE 131072 +#define X300_PACKET_SIZE_BYTES 256 +#define X300_FPGA_SECTOR_START 32 +#define X300_MAX_RESPONSE_BYTES 128 +#define UDP_TIMEOUT 3 +#define FPGA_LOAD_TIMEOUT 15 + +/* + * Packet structure + */ +typedef struct { + boost::uint32_t flags; + boost::uint32_t sector; + boost::uint32_t index; + boost::uint32_t size; + union { + boost::uint8_t data8[X300_PACKET_SIZE_BYTES]; + boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2]; + }; +} x300_fpga_update_data_t; + +/* + * X-Series burn session + */ +typedef struct { + bool found; + bool ethernet; + bool configure; // Reload FPGA after burning to flash (Ethernet only) + bool verify; // Device will verify the download along the way (Ethernet only) + bool lvbitx; + uhd::device_addr_t dev_addr; + std::string ip_addr; + std::string fpga_type; + std::string resource; + std::string filepath; + std::string rpc_port; + boost::uint32_t size; + udp_simple::sptr xport; + std::vector<char> bitstream; // .bin image extracted from .lvbitx file + boost::uint8_t data_in[udp_simple::mtu]; +} x300_session_t; + +/* + * Extract the .bin image from the given LVBITX file. + */ +static void extract_from_lvbitx(x300_session_t &session){ + boost::property_tree::ptree pt; + boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt, + boost::property_tree::xml_parser::no_comments | + boost::property_tree::xml_parser::trim_whitespace); + const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream")); + std::vector<char> decoded_bitstream(encoded_bitstream.size()); + + base64_decodestate decode_state; + base64_init_decodestate(&decode_state); + const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(), + encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state); + decoded_bitstream.resize(decoded_size); + session.bitstream.swap(decoded_bitstream); + + session.size = session.bitstream.size(); +} + +/* + * Validate X300 image and extract if LVBITX. + */ +static void x300_validate_image(x300_session_t &session){ + if(not fs::exists(session.filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") + % session.filepath)); + } + + std::string extension = fs::extension(session.filepath); + session.lvbitx = (extension == ".lvbitx"); + + if(session.lvbitx){ + extract_from_lvbitx(session); + if(session.size > X300_FPGA_BIN_SIZE_BYTES){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % X300_FPGA_BIN_SIZE_BYTES)); + } + + /* + * PCIe burning just takes a filepath, even for a .lvbitx file, + * so just extract it to validate the size. + */ + if(!session.ethernet) session.bitstream.clear(); + } + else if(extension == ".bin" or extension == ".bit"){ + boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES + : X300_FPGA_BIT_SIZE_BYTES; + + session.size = fs::file_size(session.filepath); + if(session.size > max_size){ + throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") + % session.size % max_size)); + return; + } + } + else{ + throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.") + % extension)); + } +} + +static void x300_setup_session(x300_session_t &session, + const device_addr_t &args, + const std::string &filepath){ + device_addrs_t devs = x300_find(args); + if(devs.size() == 0){ + session.found = false; + return; + } + else if(devs.size() > 1){ + std::string err_msg = "Could not resolve given args to a single X-Series device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + std::string identifier = dev.has_key("addr") ? "addr" + : "resource"; + + err_msg += str(boost::format(" * %s (%s=%s)\n") + % dev.get("product", "X3XX") + % identifier + % dev.get(identifier)); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + + session.found = true; + session.dev_addr = devs[0]; + session.ethernet = session.dev_addr.has_key("addr"); + if(session.ethernet){ + session.ip_addr = session.dev_addr["addr"]; + session.configure = args.has_key("configure"); + session.xport = udp_simple::make_connected(session.ip_addr, + BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); + session.verify = args.has_key("verify"); + } + else{ + session.resource = session.dev_addr["resource"]; + session.rpc_port = args.get("rpc-port", "5444"); + } + + /* + * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user + * does not specify one, this will default to the type currently on the device. If this + * cannot be determined, then the user is forced to specify a filename. + */ + session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", "")); + if(filepath == ""){ + if(!session.dev_addr.has_key("product") or session.fpga_type == ""){ + throw uhd::runtime_error("Found a device but could not auto-generate an image filename."); + } + else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit") + % (to_lower_copy(session.dev_addr["product"])) + % session.fpga_type)); + } + else session.filepath = filepath; + + // Validate image + x300_validate_image(session); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport, + boost::uint32_t pkt_code, + x300_fpga_update_data_t *pkt_out, + boost::uint8_t* data){ + pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code); + xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); + return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in, + size_t len){ + return (len > 0 and + ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR)); +} + +// Image data needs to be bitswapped +static UHD_INLINE void x300_bitswap(boost::uint8_t *num){ + *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4); + *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2); + *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1); +} + +static void x300_ethernet_load(x300_session_t &session){ + + // UDP receive buffer + x300_fpga_update_data_t pkt_out; + const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in); + + // Initialize write session + boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT; + size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(x300_recv_ok(pkt_in, len)){ + std::cout << "-- Initializing FPGA loading..." << std::flush; + } + else if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during initialization."); + } + + std::cout << "successful." << std::endl; + if(session.verify){ + std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl; + } + + size_t current_pos = 0; + size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE); + std::ifstream image(session.filepath.c_str(), std::ios::binary); + + // Each sector + for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){ + + // Print progress percentage at beginning of each sector + std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)") + % session.fpga_type + % (int(double(i) / double(session.size) * 100.0)) + % (i / X300_FLASH_SECTOR_SIZE) + % sectors + << std::flush; + + // Each packet + for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ + flags = X300_FPGA_PROG_FLAGS_ACK; + if(j == i) flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector + if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY; + + // Set burn location + pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); + pkt_out.index = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); + pkt_out.size = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2); + + // Read next piece of image + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + if(session.lvbitx){ + memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES); + current_pos += X300_PACKET_SIZE_BYTES; + } + else{ + image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES); + } + + // Data must be bitswapped and byteswapped + for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ + x300_bitswap(&pkt_out.data8[k]); + } + for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ + pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]); + } + + len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(len == 0){ + if(!session.lvbitx) image.close(); + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + if(!session.lvbitx) image.close(); + throw uhd::runtime_error("Device reported an error."); + } + } + } + if(!session.lvbitx){ + image.close(); + } + + std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)") + % session.fpga_type + % sectors + % sectors + << std::endl; + + // Cleanup + if(!session.lvbitx) image.close(); + flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK); + pkt_out.sector = pkt_out.index = pkt_out.size = 0; + memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); + std::cout << "-- Finalizing image load..." << std::flush; + len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error during cleanup."); + } + else std::cout << "successful." << std::endl; + + // Save new FPGA image (if option set) + if(session.configure){ + flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); + x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); + std::cout << "-- Saving image onto device..." << std::flush; + if(len == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Timed out waiting for reply from device."); + } + else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Device reported an error while saving the image."); + } + else std::cout << "successful." << std::endl; + } +} + +static void x300_pcie_load(x300_session_t &session){ + + std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...") + % session.fpga_type + << std::flush; + + nirio_status status = NiRio_Status_Success; + niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port); + nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status); + + if(nirio_status_fatal(status)){ + std::cout << "failed." << std::endl; + niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:"); + } + else std::cout << "successful." << std::endl; +} + +static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + // See if any X3x0 with the given args is found + device_addrs_t devs = x300_find(image_loader_args.args); + if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + + x300_session_t session; + x300_setup_session(session, + image_loader_args.args, + image_loader_args.fpga_path + ); + if(!session.found) return false; + + std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n") + % session.dev_addr["product"] + % session.dev_addr["serial"] + % session.dev_addr[session.ethernet ? "addr" : "resource"] + % session.filepath; + + if(session.ethernet) x300_ethernet_load(session); + else x300_pcie_load(session); + return true; +} + +UHD_STATIC_BLOCK(register_x300_image_loader){ + std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n" + "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n" + "for details on restoring your device."; + + image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index aff150acb..2b6768079 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // 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 @@ -16,7 +16,6 @@ // #include "x300_impl.hpp" -#include "x300_regs.hpp" #include "x300_lvbitx.hpp" #include "x310_lvbitx.hpp" #include <boost/algorithm/string.hpp> @@ -30,6 +29,7 @@ #include <uhd/transport/if_addrs.hpp> #include <boost/foreach.hpp> #include <boost/bind.hpp> +#include <boost/make_shared.hpp> #include <boost/functional/hash.hpp> #include <boost/assign/list_of.hpp> #include <fstream> @@ -41,12 +41,13 @@ #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" -#define X300_REV(x) (x - "A" + 1) +#define X300_REV(x) ((x) - "A" + 1) using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; using namespace uhd::niusrprio; +using namespace uhd::usrp::x300; namespace asio = boost::asio; /*********************************************************************** @@ -236,7 +237,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu return addrs; } -static device_addrs_t x300_find(const device_addr_t &hint_) +device_addrs_t x300_find(const device_addr_t &hint_) { //handle the multi-device discovery device_addrs_t hints = separate_device_addr(hint_); @@ -400,7 +401,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) default: nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \ have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \ - driver have been loaded."); + drivers have loaded successfully."); } //Load the lvbitx onto the device UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path(); @@ -410,7 +411,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //Tell the quirks object which FIFOs carry TX stream data const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; - mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos); + mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2); _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE); } @@ -508,9 +509,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) x300_load_fw(mb.zpu_ctrl, x300_fw_image); } - //check compat -- good place to do after conditional loading + //check compat numbers + //check fpga compat before fw compat because the fw is a subset of the fpga image + this->check_fpga_compat(mb_path, mb); this->check_fw_compat(mb_path, mb.zpu_ctrl); - this->check_fpga_compat(mb_path, mb.zpu_ctrl); + + mb.fw_regmap = boost::make_shared<fw_regmap_t>(); + mb.fw_regmap->initialize(*mb.zpu_ctrl.get(), true); //store which FPGA image is loaded mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -558,6 +563,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) .set(mb_eeprom) .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); + bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); + if (recover_mb_eeprom) { + UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version " + "checks.\nOperating in this mode may cause hardware damage and unstable " + "radio performance!"<< std::endl; + } + //////////////////////////////////////////////////////////////////// // parse the product number //////////////////////////////////////////////////////////////////// @@ -570,7 +582,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) product_name = "X310"; break; default: - break; + if (not recover_mb_eeprom) + throw uhd::runtime_error("Unrecognized product type.\n" + "Either the software does not support this device in which case please update your driver software to the latest version and retry OR\n" + "The product code in the EEPROM is corrupt and may require reprogramming."); } _tree->create<std::string>(mb_path / "name").set(product_name); _tree->create<std::string>(mb_path / "codename").set("Yetti"); @@ -602,37 +617,57 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) } //////////////////////////////////////////////////////////////////// - // create clock control objects + // read hardware revision and compatibility number //////////////////////////////////////////////////////////////////// - UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; - mb.hw_rev = 0; if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) { try { mb.hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]); } catch(...) { - UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl; + if (not recover_mb_eeprom) + throw uhd::runtime_error("Revision in EEPROM is invalid! Please reprogram your EEPROM."); } } else { - UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl; + if (not recover_mb_eeprom) + throw uhd::runtime_error("No revision detected. MB EEPROM must be reprogrammed!"); } - if(mb.hw_rev == 0) { - UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl; - mb.hw_rev = X300_REV("D"); + size_t hw_rev_compat = 0; + if (mb.hw_rev >= 7) { //Revision compat was added with revision 7 + if (mb_eeprom.has_key("revision_compat") and not mb_eeprom["revision_compat"].empty()) { + try { + hw_rev_compat = boost::lexical_cast<size_t>(mb_eeprom["revision_compat"]); + } catch(...) { + if (not recover_mb_eeprom) + throw uhd::runtime_error("Revision compat in EEPROM is invalid! Please reprogram your EEPROM."); + } + } else { + if (not recover_mb_eeprom) + throw uhd::runtime_error("No revision compat detected. MB EEPROM must be reprogrammed!"); + } + } else { + //For older HW just assume that revision_compat = revision + hw_rev_compat = mb.hw_rev; } - if (mb.hw_rev > X300_MAX_HW_REV) { - throw uhd::runtime_error(str( - boost::format("Unsupported board revision number: %d.\n" - "The maximum board revision number supported in this version is %d.\n" - "Please update your UHD version.") - % mb.hw_rev % X300_MAX_HW_REV - )); + if (hw_rev_compat > X300_REVISION_COMPAT) { + if (not recover_mb_eeprom) + throw uhd::runtime_error(str(boost::format( + "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.") + % mb.hw_rev)); + } else if (mb.hw_rev < X300_REVISION_MIN) { //Compare min against the revision (and not compat) to give us more leeway for partial support for a compat + if (not recover_mb_eeprom) + throw uhd::runtime_error(str(boost::format( + "Software is too new for this hardware. Please downgrade to a driver that supports hardware revision %d.") + % mb.hw_rev)); } - //Create clock control. NOTE: This does not configure the LMK yet. - initialize_clock_control(mb); + //////////////////////////////////////////////////////////////////// + // create clock control objects + //////////////////////////////////////////////////////////////////// + UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; + + //Initialize clock control registers. NOTE: This does not configure the LMK yet. mb.clock = x300_clock_ctrl::make(mb.zpu_spi, 1 /*slaveno*/, mb.hw_rev, @@ -696,23 +731,33 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //////////////////////////////////////////////////////////////////// // setup radios //////////////////////////////////////////////////////////////////// - UHD_MSG(status) << "Initialize Radio control..." << std::endl; - this->setup_radio(mb_i, "A"); - this->setup_radio(mb_i, "B"); + this->setup_radio(mb_i, "A", dev_addr); + this->setup_radio(mb_i, "B", dev_addr); + + //////////////////////////////////////////////////////////////////// + // ADC test and cal + //////////////////////////////////////////////////////////////////// + if (dev_addr.has_key("self_cal_adc_delay")) { + self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */); + } + if (dev_addr.has_key("ext_adc_self_test")) { + extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30)); + } else { + self_test_adcs(mb); + } //////////////////////////////////////////////////////////////////// // front panel gpio //////////////////////////////////////////////////////////////////// - mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); - const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); - BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) + mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::GPIO), radio::RB32_FP_GPIO); + BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) { - _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) + _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) .set(0) - .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); + .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1)); } _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") - .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); + .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio)); //////////////////////////////////////////////////////////////////// // register the time keepers - only one can be the highlander @@ -745,8 +790,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) //////////////////////////////////////////////////////////////////// _tree->create<std::string>(mb_path / "clock_source" / "value") .set("internal") - .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)) - .subscribe(boost::bind(&x300_impl::reset_radios, this, boost::ref(mb))); + .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); @@ -771,23 +815,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) .set(mb.clock->get_master_clock_rate()); //////////////////////////////////////////////////////////////////// - // initialize clock and time sources - //////////////////////////////////////////////////////////////////// - if (mb.gps and mb.gps->gps_detected()) - { - UHD_MSG(status) << "Initializing clock and time using GPSDO... " << std::flush; - _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo"); - _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); - const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int() + 1); - _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); - } else { - UHD_MSG(status) << "Initializing clock and time using internal sources... " << std::flush; - _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal"); - _tree->access<std::string>(mb_path / "time_source" / "value").set("internal"); - } - UHD_MSG(status) << "done" << std::endl; - - //////////////////////////////////////////////////////////////////// // create frontend mapping //////////////////////////////////////////////////////////////////// std::vector<size_t> default_map(2, 0); default_map[1] = 1; @@ -802,7 +829,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) // and do the misc mboard sensors //////////////////////////////////////////////////////////////////// _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") - .publish(boost::bind(&x300_impl::get_ref_locked, this, mb.zpu_ctrl)); + .publish(boost::bind(&x300_impl::get_ref_locked, this, mb)); //////////////////////////////////////////////////////////////////// // do some post-init tasks @@ -820,6 +847,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec); _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); + mb.regmap_db = boost::make_shared<uhd::soft_regmap_db_t>(); + mb.regmap_db->add(*mb.fw_regmap); + mb.regmap_db->add(*mb.radio_perifs[0].regmap); + mb.regmap_db->add(*mb.radio_perifs[1].regmap); + + _tree->create<uhd::soft_regmap_accessor_t::sptr>(mb_path / "registers") + .set(mb.regmap_db); + mb.initialization_done = true; } @@ -829,8 +864,13 @@ x300_impl::~x300_impl(void) { BOOST_FOREACH(mboard_members_t &mb, _mb) { - mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC - mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC + //Disable/reset ADC/DAC + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); + mb.radio_perifs[0].regmap->misc_outs_reg.flush(); + mb.radio_perifs[1].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); + mb.radio_perifs[1].regmap->misc_outs_reg.flush(); //kill the claimer task and unclaim the device mb.claimer_task.reset(); @@ -850,15 +890,7 @@ x300_impl::~x300_impl(void) } } -static void check_adc(wb_iface::sptr iface, const boost::uint32_t val) -{ - boost::uint32_t adc_rb = iface->peek32(RB32_RX); - adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA - //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl; - UHD_ASSERT_THROW(adc_rb == val); -} - -void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) +void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr) { const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); UHD_ASSERT_THROW(mb_i < _mb.size()); @@ -866,6 +898,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) const size_t radio_index = mb.get_radio_index(slot_name); radio_perifs_t &perif = mb.radio_perifs[radio_index]; + UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl; + //////////////////////////////////////////////////////////////////// // radio control //////////////////////////////////////////////////////////////////// @@ -873,39 +907,57 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) boost::uint32_t ctrl_sid; both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid); perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name); - perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac - perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable + + perif.regmap = boost::make_shared<radio_regmap_t>(radio_index); + perif.regmap->initialize(*perif.ctrl, true); + + //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 + if (radio_index == 0) { + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + perif.regmap->misc_outs_reg.flush(); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); + perif.regmap->misc_outs_reg.flush(); + } + perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1); this->register_loopback_self_test(perif.ctrl); - perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI); + //////////////////////////////////////////////////////////////// + // Setup peripherals + //////////////////////////////////////////////////////////////// + perif.spi = spi_core_3000::make(perif.ctrl, radio::sr_addr(radio::SPI), radio::RB32_SPI); perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN); perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); - perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS)); + perif.leds = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::LEDS)); + perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT)); + perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); + perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT)); + perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); + perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); + perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL)); + perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP)); + perif.ddc->set_link_rate(10e9/8); //whatever + perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL)); + perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP)); + perif.duc->set_link_rate(10e9/8); //whatever + + //////////////////////////////////////////////////////////////////// + // create time control objects + //////////////////////////////////////////////////////////////////// + time_core_3000::readback_bases_type time64_rb_bases; + time64_rb_bases.rb_now = radio::RB64_TIME_NOW; + time64_rb_bases.rb_pps = radio::RB64_TIME_PPS; + perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases); + + //Capture delays are calibrated every time. The status is only printed is the user + //asks to run the xfer self cal using "self_cal_adc_delay" + self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay")); _tree->access<time_spec_t>(mb_path / "time" / "cmd") .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); - - //////////////////////////////////////////////////////////////// - // ADC self test - //////////////////////////////////////////////////////////////// - perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc); - perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000); - perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000); - perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc); - for (size_t k = 0; k < 14; k++) - { - perif.adc->set_test_word("zeros", "custom", 1 << k); - check_adc(perif.ctrl, 1 << (k+2)); - } - for (size_t k = 0; k < 14; k++) - { - perif.adc->set_test_word("custom", "zeros", 1 << k); - check_adc(perif.ctrl, 1 << (k+18)); - } - perif.adc->set_test_word("normal", "normal"); //////////////////////////////////////////////////////////////// // create codec control objects @@ -922,80 +974,28 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) //////////////////////////////////////////////////////////////////// // front end corrections //////////////////////////////////////////////////////////////////// - perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); - const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; - _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") - .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) - .set(true); - _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") - .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - - perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); - const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; - _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") - .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); - _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") - .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) - .set(std::complex<double>(0.0, 0.0)); + perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); + perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name)); //////////////////////////////////////////////////////////////////// - // create rx dsp control objects + // connect rx dsp control objects //////////////////////////////////////////////////////////////////// - perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL)); - perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP)); - perif.ddc->set_link_rate(10e9/8); //whatever - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) - .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % radio_index); - _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); - _tree->create<double>(rx_dsp_path / "rate" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) + perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); + _tree->access<double>(rx_dsp_path / "rate" / "value") .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1)) - .set(1e6); - _tree->create<double>(rx_dsp_path / "freq" / "value") - .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) - .set(0.0); - _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") - .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); + ; _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); //////////////////////////////////////////////////////////////////// - // create tx dsp control objects + // connect tx dsp control objects //////////////////////////////////////////////////////////////////// - perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); - perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP)); - perif.duc->set_link_rate(10e9/8); //whatever - _tree->access<double>(mb_path / "tick_rate") - .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) - .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % radio_index); - _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); - _tree->create<double>(tx_dsp_path / "rate" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) + perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); + _tree->access<double>(tx_dsp_path / "rate" / "value") .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1)) - .set(1e6); - _tree->create<double>(tx_dsp_path / "freq" / "value") - .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) - .set(0.0); - _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") - .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); - - //////////////////////////////////////////////////////////////////// - // create time control objects - //////////////////////////////////////////////////////////////////// - time_core_3000::readback_bases_type time64_rb_bases; - time64_rb_bases.rb_now = RB64_TIME_NOW; - time64_rb_bases.rb_pps = RB64_TIME_PPS; - perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases); + ; //////////////////////////////////////////////////////////////////// // create RF frontend interfacing @@ -1014,7 +1014,7 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) //create a new dboard interface x300_dboard_iface_config_t db_config; - db_config.gpio = gpio_core_200::make(perif.ctrl, TOREG(SR_GPIO), RB32_GPIO); + db_config.gpio = gpio_core_200::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO); db_config.spi = perif.spi; db_config.rx_spi_slaveno = DB_RX_SEN; db_config.tx_spi_slaveno = DB_TX_SEN; @@ -1309,8 +1309,14 @@ void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) { - BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) + BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) { + perif.ctrl->set_tick_rate(rate); perif.time64->set_tick_rate(rate); + perif.framer->set_tick_rate(rate); + perif.ddc->set_tick_rate(rate); + perif.deframer->set_tick_rate(rate); + perif.duc->set_tick_rate(rate); + } } void x300_impl::register_loopback_self_test(wb_iface::sptr iface) @@ -1321,8 +1327,8 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface) for (size_t i = 0; i < 100; i++) { boost::hash_combine(hash, i); - iface->poke32(TOREG(SR_TEST), boost::uint32_t(hash)); - test_fail = iface->peek32(RB32_TEST) != boost::uint32_t(hash); + iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash)); + test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash); if (test_fail) break; //exit loop on any failure } UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; @@ -1332,32 +1338,9 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface) * clock and time control logic **********************************************************************/ -void x300_impl::update_clock_control(mboard_members_t &mb) -{ - const size_t reg = mb.clock_control_regs_clock_source - | (mb.clock_control_regs_pps_select << 2) - | (mb.clock_control_regs_pps_out_enb << 4) - | (mb.clock_control_regs_tcxo_enb << 5) - | (mb.clock_control_regs_gpsdo_pwr << 6) - ; - mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg); -} - -void x300_impl::initialize_clock_control(mboard_members_t &mb) -{ - //Initialize clock control register soft copies - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; - mb.clock_control_regs_pps_out_enb = 0; - mb.clock_control_regs_tcxo_enb = 1; - mb.clock_control_regs_gpsdo_pwr = 1; //GPSDO power always ON - this->update_clock_control(mb); -} - void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb) { - mb.clock_control_regs_pps_out_enb = enb? 1 : 0; - this->update_clock_control(mb); + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb?1:0); } void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source) @@ -1365,21 +1348,22 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou //Optimize for the case when the current source is internal and we are trying //to set it to internal. This is the only case where we are guaranteed that //the clock has not gone away so we can skip setting the MUX and reseting the LMK. - if (not (mb.current_refclk_src == "internal" and source == "internal")) { + const bool reconfigure_clks = (mb.current_refclk_src != "internal") or (source != "internal"); + if (reconfigure_clks) { //Update the clock MUX on the motherboard to select the requested source - mb.clock_control_regs_clock_source = 0; - mb.clock_control_regs_tcxo_enb = 0; if (source == "internal") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL; - mb.clock_control_regs_tcxo_enb = 1; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 1); } else if (source == "external") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); } else if (source == "gpsdo") { - mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO; + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::CLK_SOURCE, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); + mb.fw_regmap->clock_ctrl_reg.set(fw_regmap_t::clk_ctrl_reg_t::TCXO_EN, 0); } else { throw uhd::key_error("update_clock_source: unknown source: " + source); } - this->update_clock_control(mb); + mb.fw_regmap->clock_ctrl_reg.flush(); //Reset the LMK to make sure it re-locks to the new reference mb.clock->reset_clocks(); @@ -1394,10 +1378,10 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou //The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may //lead to locking issues. So, disable the ref-locked check for older (unsupported) boards. if (mb.hw_rev > 4) { - if (not wait_for_ref_locked(mb.zpu_ctrl, timeout)) { + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, timeout)) { //failed to lock on reference if (mb.initialization_done) { - throw uhd::runtime_error((boost::format("Reference Clock failed to lock to %s source.") % source).str()); + throw uhd::runtime_error((boost::format("Reference Clock PLL failed to lock to %s source.") % source).str()); } else { //TODO: Re-enable this warning when we figure out a reliable lock time //UHD_MSG(warning) << "Reference clock failed to lock to " + source + " during device initialization. " << @@ -1406,6 +1390,41 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou } } + if (reconfigure_clks) { + //Reset the radio clock PLL in the FPGA + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + + //Wait for radio clock PLL to lock + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK, 0.01)) { + throw uhd::runtime_error((boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") % source).str()); + } + + //Reset the IDELAYCTRL used to calibrate the data interface delays + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL); + mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + + //Wait for the ADC IDELAYCTRL to be ready + if (not wait_for_clk_locked(mb, fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK, 0.01)) { + throw uhd::runtime_error((boost::format("ADC Calibration Clock in FPGA failed to lock to %s source.") % source).str()); + } + + // Reset ADCs and DACs + for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { + radio_perifs_t &perif = mb.radio_perifs[r]; + if (perif.regmap && r==0) { //ADC/DAC reset lines only exist in Radio0 + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); + perif.regmap->misc_outs_reg.flush(); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); + perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); + perif.regmap->misc_outs_reg.flush(); + } + if (perif.adc) perif.adc->reset(); + if (perif.dac) perif.dac->reset(); + } + } + //Update cache value mb.current_refclk_src = source; } @@ -1413,119 +1432,60 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source) { if (source == "internal") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_INTERNAL); } else if (source == "external") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_EXTERNAL); } else if (source == "gpsdo") { - mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO; + mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_SELECT, fw_regmap_t::clk_ctrl_reg_t::SRC_GPSDO); } else { throw uhd::key_error("update_time_source: unknown source: " + source); } - this->update_clock_control(mb); - + /* TODO - Implement intelligent PPS detection //check for valid pps - if (!is_pps_present(mb.zpu_ctrl)) - { - // TODO - Implement intelligent PPS detection - /* throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); */ + if (!is_pps_present(mb)) { + throw uhd::runtime_error((boost::format("The %d PPS was not detected. Please check the PPS source and try again.") % source).str()); } + */ } -bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout) +bool x300_impl::wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout) { boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0); - do - { - if (get_ref_locked(ctrl).to_bool()) + do { + if (mb.fw_regmap->clock_status_reg.read(which)==1) return true; boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } while (boost::get_system_time() < timeout_time); - //failed to lock on reference - return false; + //Check one last time + return (mb.fw_regmap->clock_status_reg.read(which)==1); } -sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl) +sensor_value_t x300_impl::get_ref_locked(mboard_members_t& mb) { - boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); - const bool lock = ((clk_status & ZPU_RB_CLK_STATUS_LMK_LOCK) != 0); + mb.fw_regmap->clock_status_reg.refresh(); + const bool lock = (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::LMK_LOCK)==1) && + (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::RADIO_CLK_LOCK)==1) && + (mb.fw_regmap->clock_status_reg.get(fw_regmap_t::clk_status_reg_t::IDELAYCTRL_LOCK)==1); return sensor_value_t("Ref", lock, "locked", "unlocked"); } -bool x300_impl::is_pps_present(wb_iface::sptr ctrl) +bool x300_impl::is_pps_present(mboard_members_t& mb) { // The ZPU_RB_CLK_STATUS_PPS_DETECT bit toggles with each rising edge of the PPS. // We monitor it for up to 1.5 seconds looking for it to toggle. - boost::uint32_t pps_detect = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & ZPU_RB_CLK_STATUS_PPS_DETECT; + boost::uint32_t pps_detect = mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT); for (int i = 0; i < 15; i++) { boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); - if (pps_detect != (clk_status & ZPU_RB_CLK_STATUS_PPS_DETECT)) + if (pps_detect != mb.fw_regmap->clock_status_reg.read(fw_regmap_t::clk_status_reg_t::PPS_DETECT)) return true; } return false; } /*********************************************************************** - * reset and synchronization logic - **********************************************************************/ - -void x300_impl::reset_radios(mboard_members_t &mb) -{ - // Reset ADCs and DACs - BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs) - { - perif.adc->reset(); - perif.dac->reset(); - } -} - -void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) -{ - if (radios.size() < 2) return; //Nothing to synchronize - - //**PRECONDITION** - //This function assumes that all the VITA times in "radios" are synchronized - //to a common reference. Currently, this function is called in get_tx_stream - //which also has the same precondition. - - //Reinitialize and resync all DACs - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->dac->reset_and_resync(); - } - - //Get a rough estimate of the cumulative command latency - boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call - } - boost::posix_time::time_duration t_elapsed = - boost::posix_time::microsec_clock::local_time() - t_start; - - //Add 100% of headroom + uncertaintly to the command time - boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; - - //Pick radios[0] as the time reference. - uhd::time_spec_t sync_time = - radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); - - //Send the sync command - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->ctrl->set_time(sync_time); - radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1); //Arm FRAMEP/N sync pulse - radios[i]->ctrl->set_time(uhd::time_spec_t(0.0)); //Clear command time - } - - //Wait and check status - boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); - for (size_t i = 0; i < radios.size(); i++) { - radios[i]->dac->verify_sync(); - } -} - -/*********************************************************************** * eeprom **********************************************************************/ @@ -1544,20 +1504,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep * front-panel GPIO **********************************************************************/ -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio) { return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); } -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) { - if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); - if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); - if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); - if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); - if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); - if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); - if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + switch (attr) + { + case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); + case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); + case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); + case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); + case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); + case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); + case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); + default: UHD_THROW_INVALID_CODE_PATH(); + } } /*********************************************************************** @@ -1688,25 +1652,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface) % compat_major % compat_minor)); } -void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface) +void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members) { - boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); + boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); boost::uint32_t compat_major = (compat_num >> 16); boost::uint32_t compat_minor = (compat_num & 0xffff); if (compat_major != X300_FPGA_COMPAT_MAJOR) { + std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"") + % image_loader_path + % (members.xport_path == "eth" ? "addr" + : "resource") + % members.addr); + throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA image on your device is not compatible with this host code build.\n" "Download the appropriate FPGA images for this version of UHD.\n" "%s\n\n" "Then burn a new image to the on-board flash storage of your\n" - "USRP X3xx device using the burner utility. %s\n\n" + "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n" "For more information, refer to the UHD manual:\n\n" " http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash" ) % int(X300_FPGA_COMPAT_MAJOR) % compat_major - % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner"))); + % print_utility_error("uhd_images_downloader.py") + % image_loader_cmd)); } _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % compat_major % compat_minor)); @@ -1727,17 +1699,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res if (nirio_status_not_fatal(status)) { //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping switch (pid) { - case X300_USRP_PCIE_SSID: + case X300_USRP_PCIE_SSID_ADC_33: + case X300_USRP_PCIE_SSID_ADC_18: mb_type = USRP_X300_MB; break; - case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_USRP_PCIE_SSID_ADC_33: + case X310_2940R_40MHz_PCIE_SSID_ADC_33: + case X310_2940R_120MHz_PCIE_SSID_ADC_33: + case X310_2942R_40MHz_PCIE_SSID_ADC_33: + case X310_2942R_120MHz_PCIE_SSID_ADC_33: + case X310_2943R_40MHz_PCIE_SSID_ADC_33: + case X310_2943R_120MHz_PCIE_SSID_ADC_33: + case X310_2944R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_120MHz_PCIE_SSID_ADC_33: + case X310_2952R_40MHz_PCIE_SSID_ADC_33: + case X310_2952R_120MHz_PCIE_SSID_ADC_33: + case X310_2953R_40MHz_PCIE_SSID_ADC_33: + case X310_2953R_120MHz_PCIE_SSID_ADC_33: + case X310_2954R_40MHz_PCIE_SSID_ADC_33: + case X310_USRP_PCIE_SSID_ADC_18: + case X310_2940R_40MHz_PCIE_SSID_ADC_18: + case X310_2940R_120MHz_PCIE_SSID_ADC_18: + case X310_2942R_40MHz_PCIE_SSID_ADC_18: + case X310_2942R_120MHz_PCIE_SSID_ADC_18: + case X310_2943R_40MHz_PCIE_SSID_ADC_18: + case X310_2943R_120MHz_PCIE_SSID_ADC_18: + case X310_2944R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_120MHz_PCIE_SSID_ADC_18: + case X310_2952R_40MHz_PCIE_SSID_ADC_18: + case X310_2952R_120MHz_PCIE_SSID_ADC_18: + case X310_2953R_40MHz_PCIE_SSID_ADC_18: + case X310_2953R_120MHz_PCIE_SSID_ADC_18: + case X310_2954R_40MHz_PCIE_SSID_ADC_18: mb_type = USRP_X310_MB; break; default: mb_type = UNKNOWN; break; @@ -1762,17 +1756,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo switch (product_num) { //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping - case X300_USRP_PCIE_SSID: + case X300_USRP_PCIE_SSID_ADC_33: + case X300_USRP_PCIE_SSID_ADC_18: mb_type = USRP_X300_MB; break; - case X310_USRP_PCIE_SSID: - case X310_2940R_PCIE_SSID: - case X310_2942R_PCIE_SSID: - case X310_2943R_PCIE_SSID: - case X310_2944R_PCIE_SSID: - case X310_2950R_PCIE_SSID: - case X310_2952R_PCIE_SSID: - case X310_2953R_PCIE_SSID: - case X310_2954R_PCIE_SSID: + case X310_USRP_PCIE_SSID_ADC_33: + case X310_2940R_40MHz_PCIE_SSID_ADC_33: + case X310_2940R_120MHz_PCIE_SSID_ADC_33: + case X310_2942R_40MHz_PCIE_SSID_ADC_33: + case X310_2942R_120MHz_PCIE_SSID_ADC_33: + case X310_2943R_40MHz_PCIE_SSID_ADC_33: + case X310_2943R_120MHz_PCIE_SSID_ADC_33: + case X310_2944R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_40MHz_PCIE_SSID_ADC_33: + case X310_2950R_120MHz_PCIE_SSID_ADC_33: + case X310_2952R_40MHz_PCIE_SSID_ADC_33: + case X310_2952R_120MHz_PCIE_SSID_ADC_33: + case X310_2953R_40MHz_PCIE_SSID_ADC_33: + case X310_2953R_120MHz_PCIE_SSID_ADC_33: + case X310_2954R_40MHz_PCIE_SSID_ADC_33: + case X310_USRP_PCIE_SSID_ADC_18: + case X310_2940R_40MHz_PCIE_SSID_ADC_18: + case X310_2940R_120MHz_PCIE_SSID_ADC_18: + case X310_2942R_40MHz_PCIE_SSID_ADC_18: + case X310_2942R_120MHz_PCIE_SSID_ADC_18: + case X310_2943R_40MHz_PCIE_SSID_ADC_18: + case X310_2943R_120MHz_PCIE_SSID_ADC_18: + case X310_2944R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_40MHz_PCIE_SSID_ADC_18: + case X310_2950R_120MHz_PCIE_SSID_ADC_18: + case X310_2952R_40MHz_PCIE_SSID_ADC_18: + case X310_2952R_120MHz_PCIE_SSID_ADC_18: + case X310_2953R_40MHz_PCIE_SSID_ADC_18: + case X310_2953R_120MHz_PCIE_SSID_ADC_18: + case X310_2954R_40MHz_PCIE_SSID_ADC_18: mb_type = USRP_X310_MB; break; default: UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; @@ -1781,4 +1797,3 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo } return mb_type; } - diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 890ef7bcb..78c497ad9 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // 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 @@ -49,6 +49,7 @@ #include <uhd/transport/nirio/niusrprio_session.h> #include <uhd/transport/vrt_if_packet.hpp> #include "recv_packet_demuxer_3000.hpp" +#include "x300_regs.hpp" static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin"; @@ -140,6 +141,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface); uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp); uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy); +uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_); + class x300_impl : public uhd::device { public: @@ -172,6 +175,7 @@ private: //perifs in the radio core struct radio_perifs_t { + //Interfaces radio_ctrl_core_3000::sptr ctrl; spi_core_3000::sptr spi; x300_adc_ctrl::sptr adc; @@ -184,6 +188,8 @@ private: gpio_core_200_32wo::sptr leds; rx_frontend_core_200::sptr rx_fe; tx_frontend_core_200::sptr tx_fe; + //Registers + uhd::usrp::x300::radio_regmap_t::sptr regmap; }; //overflow recovery impl @@ -211,7 +217,8 @@ private: i2c_core_100_wb32::sptr zpu_i2c; //perifs in each radio - radio_perifs_t radio_perifs[2]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B + static const size_t NUM_RADIOS = 2; + radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B uhd::usrp::dboard_eeprom_t db_eeproms[8]; //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs size_t get_radio_index(const std::string &slot_name) { @@ -224,18 +231,15 @@ private: uhd::gps_ctrl::sptr gps; gpio_core_200::sptr fp_gpio; - //clock control register bits - int clock_control_regs_clock_source; - int clock_control_regs_pps_select; - int clock_control_regs_pps_out_enb; - int clock_control_regs_tcxo_enb; - int clock_control_regs_gpsdo_pwr; + uhd::usrp::x300::fw_regmap_t::sptr fw_regmap; //which FPGA image is loaded std::string loaded_fpga_image; size_t hw_rev; std::string current_refclk_src; + + uhd::soft_regmap_db_t::sptr regmap_db; }; std::vector<mboard_members_t> _mb; @@ -259,7 +263,7 @@ private: * \param mb_i Motherboard index * \param slot_name Slot name (A or B). */ - void setup_radio(const size_t, const std::string &slot_name); + void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr); size_t _sid_framer; struct sid_config_t @@ -348,21 +352,26 @@ private: void set_time_source_out(mboard_members_t&, const bool); void update_clock_source(mboard_members_t&, const std::string &); void update_time_source(mboard_members_t&, const std::string &); - void reset_radios(mboard_members_t&); - uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); - bool wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0); - bool is_pps_present(uhd::wb_iface::sptr); + uhd::sensor_value_t get_ref_locked(mboard_members_t& mb); + bool wait_for_clk_locked(mboard_members_t& mb, boost::uint32_t which, double timeout); + bool is_pps_present(mboard_members_t& mb); void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &); void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &); void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); - void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); + void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members); void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); - boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); - void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); + boost::uint32_t get_fp_gpio(gpio_core_200::sptr); + void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + + void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false); + double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); + void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100); + + void extended_adc_test(mboard_members_t& mb, double duration_s); //**PRECONDITION** //This function assumes that all the VITA times in "radios" are synchronized diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 334ae8168..e3515af0c 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -23,6 +23,7 @@ #include <uhd/transport/nirio_zero_copy.hpp> #include "async_packet_handler.hpp" #include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/chdr.hpp> #include <boost/bind.hpp> #include <uhd/utils/tasks.hpp> #include <uhd/utils/log.hpp> @@ -124,41 +125,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i, /*********************************************************************** - * VITA stuff - **********************************************************************/ -static void x300_if_hdr_unpack_be( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_be( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_unpack_le( - const boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_le( - boost::uint32_t *packet_buff, - vrt::if_packet_info_t &if_packet_info -){ - if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; - return vrt::if_hdr_pack_le(packet_buff, if_packet_info); -} - -/*********************************************************************** * RX flow control handler **********************************************************************/ static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) @@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo //load header if (big_endian) - x300_if_hdr_pack_be(pkt, packet_info); + vrt::chdr::if_hdr_pack_be(pkt, packet_info); else - x300_if_hdr_pack_le(pkt, packet_info); + vrt::chdr::if_hdr_pack_le(pkt, packet_info); //load payload pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); @@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero { if (big_endian) { - x300_if_hdr_unpack_be(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info); endian_conv = uhd::ntohx; } else { - x300_if_hdr_unpack_le(packet_buff, if_packet_info); + vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info); endian_conv = uhd::wtohx; } } @@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_) //init some streamer stuff std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); + my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); conv_endianness = "le"; } @@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) std::string conv_endianness; if (mb.if_pkt_is_big_endian) { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); conv_endianness = "be"; } else { - my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); + my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); conv_endianness = "le"; } diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index f920b5ae2..eba30abb5 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -18,108 +18,133 @@ #ifndef INCLUDED_X300_REGS_HPP #define INCLUDED_X300_REGS_HPP +#include <uhd/config.hpp> #include <stdint.h> -#include <boost/cstdint.hpp> +#include <uhd/utils/soft_register.hpp> -#define TOREG(x) ((x)*4) +namespace uhd { namespace usrp { namespace radio { + +static UHD_INLINE uint32_t sr_addr(const uint32_t offset) +{ + return offset * 4; +} + +static const uint32_t DACSYNC = 5; +static const uint32_t LOOPBACK = 6; +static const uint32_t TEST = 7; +static const uint32_t SPI = 8; +static const uint32_t GPIO = 16; +static const uint32_t MISC_OUTS = 24; +static const uint32_t READBACK = 32; +static const uint32_t TX_CTRL = 64; +static const uint32_t RX_CTRL = 96; +static const uint32_t TIME = 128; +static const uint32_t RX_DSP = 144; +static const uint32_t TX_DSP = 184; +static const uint32_t LEDS = 195; +static const uint32_t FP_GPIO = 200; +static const uint32_t RX_FRONT = 208; +static const uint32_t TX_FRONT = 216; + +static const uint32_t RB32_GPIO = 0; +static const uint32_t RB32_SPI = 4; +static const uint32_t RB64_TIME_NOW = 8; +static const uint32_t RB64_TIME_PPS = 16; +static const uint32_t RB32_TEST = 24; +static const uint32_t RB32_RX = 28; +static const uint32_t RB32_FP_GPIO = 32; +static const uint32_t RB32_MISC_INS = 36; + +}}} // namespace #define localparam static const int -localparam SR_DACSYNC = 5; -localparam SR_LOOPBACK = 6; -localparam SR_TEST = 7; -localparam SR_SPI = 8; -localparam SR_GPIO = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK = 32; -localparam SR_TX_CTRL = 64; -localparam SR_RX_CTRL = 96; -localparam SR_TIME = 128; -localparam SR_RX_DSP = 144; -localparam SR_TX_DSP = 184; -localparam SR_LEDS = 195; -localparam SR_FP_GPIO = 200; -localparam SR_RX_FRONT = 208; -localparam SR_TX_FRONT = 216; - -localparam RB32_GPIO = 0; -localparam RB32_SPI = 4; -localparam RB64_TIME_NOW = 8; -localparam RB64_TIME_PPS = 16; -localparam RB32_TEST = 24; -localparam RB32_RX = 28; -localparam RB32_FP_GPIO = 32; - -localparam BL_ADDRESS = 0; -localparam BL_DATA = 1; +localparam BL_ADDRESS = 0; +localparam BL_DATA = 1; //wishbone settings map - relevant to host code -#define SET0_BASE 0xa000 -#define SETXB_BASE 0xb000 -#define BOOT_LDR_BASE 0xFA00 -#define I2C0_BASE 0xfe00 -#define I2C1_BASE 0xff00 +#define SET0_BASE 0xa000 +#define SETXB_BASE 0xb000 +#define BOOT_LDR_BASE 0xfa00 +#define I2C0_BASE 0xfe00 +#define I2C1_BASE 0xff00 #define SR_ADDR(base, offset) ((base) + (offset)*4) localparam ZPU_SR_LEDS = 00; -localparam ZPU_SR_PHY_RST = 01; +localparam ZPU_SR_SW_RST = 01; localparam ZPU_SR_CLOCK_CTRL = 02; localparam ZPU_SR_XB_LOCAL = 03; localparam ZPU_SR_SPI = 32; localparam ZPU_SR_ETHINT0 = 40; localparam ZPU_SR_ETHINT1 = 56; -//clock controls -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO 0x03 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO 0x03 +//reset bits +#define ZPU_SR_SW_RST_ETH_PHY (1<<0) +#define ZPU_SR_SW_RST_RADIO_RST (1<<1) +#define ZPU_SR_SW_RST_RADIO_CLK_PLL (1<<2) +#define ZPU_SR_SW_RST_ADC_IDELAYCTRL (1<<3) -localparam ZPU_RB_SPI = 2; +localparam ZPU_RB_SPI = 2; localparam ZPU_RB_CLK_STATUS = 3; localparam ZPU_RB_COMPAT_NUM = 6; localparam ZPU_RB_ETH_TYPE0 = 4; localparam ZPU_RB_ETH_TYPE1 = 5; -//clock status -#define ZPU_RB_CLK_STATUS_LMK_STATUS (0x3 << 0) -#define ZPU_RB_CLK_STATUS_LMK_LOCK (0x1 << 2) -#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER (0x1 << 3) -#define ZPU_RB_CLK_STATUS_PPS_DETECT (0x1 << 4) - //spi slaves on radio -#define DB_DAC_SEN (1 << 7) -#define DB_ADC_SEN (1 << 6) +#define DB_DAC_SEN (1 << 7) +#define DB_ADC_SEN (1 << 6) #define DB_RX_LSADC_SEN (1 << 5) #define DB_RX_LSDAC_SEN (1 << 4) #define DB_TX_LSADC_SEN (1 << 3) #define DB_TX_LSDAC_SEN (1 << 2) -#define DB_RX_SEN (1 << 1) -#define DB_TX_SEN (1 << 0) +#define DB_RX_SEN (1 << 1) +#define DB_TX_SEN (1 << 0) //------------------------------------------------------------------- // PCIe Registers //------------------------------------------------------------------- -static const uint32_t X300_PCIE_VID = 0x1093; -static const uint32_t X300_PCIE_PID = 0xC4C4; -static const uint32_t X300_USRP_PCIE_SSID = 0x7736; -static const uint32_t X310_USRP_PCIE_SSID = 0x76CA; -static const uint32_t X310_2940R_PCIE_SSID = 0x772B; -static const uint32_t X310_2942R_PCIE_SSID = 0x772C; -static const uint32_t X310_2943R_PCIE_SSID = 0x772D; -static const uint32_t X310_2944R_PCIE_SSID = 0x772E; -static const uint32_t X310_2950R_PCIE_SSID = 0x772F; -static const uint32_t X310_2952R_PCIE_SSID = 0x7730; -static const uint32_t X310_2953R_PCIE_SSID = 0x7731; -static const uint32_t X310_2954R_PCIE_SSID = 0x7732; +static const uint32_t X300_PCIE_VID = 0x1093; +static const uint32_t X300_PCIE_PID = 0xC4C4; +//Rev 0-6 motherboard/PCIe IDs (ADC driven at 3.3V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_33 = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID_ADC_33 = 0x76CA; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_33 = 0x772B; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_33 = 0x77FB; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_33 = 0x772C; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_33 = 0x77FC; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_33 = 0x772D; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_33 = 0x77FD; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_33 = 0x772E; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_33 = 0x772F; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_33 = 0x77FE; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_33 = 0x7730; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_33 = 0x77FF; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_33 = 0x7731; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_33 = 0x7800; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_33 = 0x7732; +//Rev 7+ motherboard/PCIe IDs (ADCs driven at 1.8V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_18 = 0x7861; +static const uint32_t X310_USRP_PCIE_SSID_ADC_18 = 0x7862; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_18 = 0x7853; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_18 = 0x785B; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_18 = 0x7854; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_18 = 0x785C; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_18 = 0x7855; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_18 = 0x785D; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_18 = 0x7856; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_18 = 0x7857; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_18 = 0x785E; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_18 = 0x7858; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_18 = 0x785F; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_18 = 0x7859; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_18 = 0x7860; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_18 = 0x785A; static const uint32_t FPGA_X3xx_SIG_VALUE = 0x58333030; static const uint32_t PCIE_FPGA_ADDR_BASE = 0xC0000; -#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + X) +#define PCIE_FPGA_REG(X) (PCIE_FPGA_ADDR_BASE + (X)) static const uint32_t FPGA_PCIE_SIG_REG = PCIE_FPGA_REG(0x0000); static const uint32_t FPGA_CNTR_LO_REG = PCIE_FPGA_REG(0x0004); @@ -140,8 +165,8 @@ static const uint32_t DMA_FRAME_SIZE_REG = 0x4; static const uint32_t DMA_SAMPLE_COUNT_REG = 0x8; static const uint32_t DMA_PKT_COUNT_REG = 0xC; -#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) -#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_TX_DMA_REG(REG, CHAN) (PCIE_TX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) +#define PCIE_RX_DMA_REG(REG, CHAN) (PCIE_RX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) static const uint32_t DMA_CTRL_DISABLED = 0x00000000; static const uint32_t DMA_CTRL_ENABLED = 0x00000002; @@ -154,20 +179,112 @@ static const uint32_t DMA_STATUS_ERROR = 0x00000001; static const uint32_t DMA_STATUS_BUSY = 0x00000002; static const uint32_t PCIE_ROUTER_REG_BASE = PCIE_FPGA_REG(0x0500); -#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + X) +#define PCIE_ROUTER_REG(X) (PCIE_ROUTER_REG_BASE + (X)) static const uint32_t PCIE_ZPU_DATA_BASE = 0x30000; static const uint32_t PCIE_ZPU_READ_BASE = 0x20000; //Trig and Status share the same base static const uint32_t PCIE_ZPU_STATUS_BASE = 0x20000; -#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) -#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) -#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) +#define PCIE_ZPU_DATA_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + (X)) +#define PCIE_ZPU_READ_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + (X)) +#define PCIE_ZPU_STATUS_REG(X) (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + (X)) static const uint32_t PCIE_ZPU_READ_START = 0x0; static const uint32_t PCIE_ZPU_READ_CLOBBER = 0x80000000; static const uint32_t PCIE_ZPU_STATUS_BUSY = 0x1; static const uint32_t PCIE_ZPU_STATUS_SUSPENDED = 0x80000000; +//------------------------------------------------------------------- +// Register Maps +//------------------------------------------------------------------- +namespace uhd { namespace usrp { namespace x300 { + class fw_regmap_t : public uhd::soft_regmap_t { + public: + typedef boost::shared_ptr<fw_regmap_t> sptr; + + class clk_ctrl_reg_t : public uhd::soft_reg32_wo_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(CLK_SOURCE, /*width*/ 2, /*shift*/ 0); //[1:0] + UHD_DEFINE_SOFT_REG_FIELD(PPS_SELECT, /*width*/ 2, /*shift*/ 2); //[3:2] + UHD_DEFINE_SOFT_REG_FIELD(PPS_OUT_EN, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(TCXO_EN, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(GPSDO_PWR_EN, /*width*/ 1, /*shift*/ 6); //[6] + + static const boost::uint32_t SRC_EXTERNAL = 0x0; + static const boost::uint32_t SRC_INTERNAL = 0x2; + static const boost::uint32_t SRC_GPSDO = 0x3; + + clk_ctrl_reg_t(): uhd::soft_reg32_wo_t(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL)) { + //Initial values + set(CLK_SOURCE, SRC_INTERNAL); + set(PPS_SELECT, SRC_INTERNAL); + set(PPS_OUT_EN, 0); + set(TCXO_EN, 1); + set(GPSDO_PWR_EN, 1); //GPSDO power always ON + } + } clock_ctrl_reg; + + class clk_status_reg_t : public uhd::soft_reg32_ro_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(LMK_STATUS, /*width*/ 2, /*shift*/ 0); //[1:0] + UHD_DEFINE_SOFT_REG_FIELD(LMK_LOCK, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(LMK_HOLDOVER, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(PPS_DETECT, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(RADIO_CLK_LOCK, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(IDELAYCTRL_LOCK, /*width*/ 1, /*shift*/ 6); //[6] + + clk_status_reg_t(): uhd::soft_reg32_ro_t(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) {} + } clock_status_reg; + + fw_regmap_t() : soft_regmap_t("fw_regmap") { + add_to_map(clock_ctrl_reg, "clock_ctrl_reg", PUBLIC); + add_to_map(clock_status_reg, "clock_status_reg", PUBLIC); + } + }; + + class radio_regmap_t : public uhd::soft_regmap_t { + public: + typedef boost::shared_ptr<radio_regmap_t> sptr; + class misc_outs_reg_t : public uhd::soft_reg32_wo_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0] + UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1] + UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9] + + misc_outs_reg_t(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::MISC_OUTS)) { + //Initial values + set(DAC_ENABLED, 0); + set(DAC_RESET_N, 0); + set(ADC_RESET, 0); + set(ADC_DATA_DLY_STB, 0); + set(ADC_DATA_DLY_VAL, 16); + set(ADC_CHECKER_ENABLED, 0); + } + } misc_outs_reg; + + class misc_ins_reg_t : public uhd::soft_reg32_ro_t { + public: + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0); //[0] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1); //[1] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2); //[2] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3); //[3] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 4); //[4] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 5); //[5] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 6); //[6] + UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 7); //[7] + + misc_ins_reg_t(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { } + } misc_ins_reg; + + radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") { + add_to_map(misc_outs_reg, "misc_outs_reg", PUBLIC); + add_to_map(misc_ins_reg, "misc_ins_reg", PUBLIC); + } + }; + +}}} #endif /* INCLUDED_X300_REGS_HPP */ diff --git a/host/lib/usrp_clock/CMakeLists.txt b/host/lib/usrp_clock/CMakeLists.txt index 8a58aa9ac..ba0f5fe0a 100644 --- a/host/lib/usrp_clock/CMakeLists.txt +++ b/host/lib/usrp_clock/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2014 Ettus Research LLC +# Copyright 2011-2015 Ettus Research LLC # # 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 @@ -23,4 +23,10 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp_clock.cpp ) +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/usrp_clock_c.cpp + ) +ENDIF(ENABLE_C_API) + INCLUDE_SUBDIRECTORY(octoclock) diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt index e363bb9da..a74cb034f 100644 --- a/host/lib/usrp_clock/octoclock/CMakeLists.txt +++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011-2014 Ettus Research LLC +# Copyright 2013-2015 Ettus Research LLC # # 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 @@ -16,17 +16,17 @@ # ######################################################################## -# This file included, use CMake directory variables -######################################################################## - -######################################################################## # Conditionally configure the OctoClock support ######################################################################## -LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF) +LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF) IF(ENABLE_OCTOCLOCK) + ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/kk_ihex_read.c ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp ) diff --git a/host/lib/usrp_clock/octoclock/common.h b/host/lib/usrp_clock/octoclock/common.h index 96acbb30f..5861bc4b1 100644 --- a/host/lib/usrp_clock/octoclock/common.h +++ b/host/lib/usrp_clock/octoclock/common.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Ettus Research LLC + * Copyright 2014-2015 Ettus Research LLC * * 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 @@ -42,11 +42,11 @@ extern "C" { * only valid C code should go in this section. */ -//These values are placed in the octoclock_packet_t.proto_ver field +// These values are placed in the octoclock_packet_t.proto_ver field #define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234 -#define OCTOCLOCK_FW_COMPAT_NUM 2 +#define OCTOCLOCK_FW_COMPAT_NUM 3 -//UDP ports assigned for different tasks +// UDP ports assigned for different tasks #define OCTOCLOCK_UDP_CTRL_PORT 50000 #define OCTOCLOCK_UDP_GPSDO_PORT 50001 #define OCTOCLOCK_UDP_FW_PORT 50002 @@ -98,11 +98,21 @@ typedef enum { } ref_t; typedef enum { - UP, - DOWN + PREFER_INTERNAL, + PREFER_EXTERNAL } switch_pos_t; +/* + * Some versions of AVR-GCC ignore #pragma pack, so + * if AVR-GCC is being used, use __attribute__ + * instead. + */ +#ifdef AVR +#define __AVR_ALIGNED__ __attribute__((aligned(1))) +#else +#define __AVR_ALIGNED__ #pragma pack(push,1) +#endif // Structure of values in EEPROM, starting in location 0 typedef struct { @@ -113,34 +123,37 @@ typedef struct { uint8_t serial[10]; uint8_t name[10]; uint8_t revision; -} octoclock_fw_eeprom_t; +} octoclock_fw_eeprom_t __AVR_ALIGNED__; typedef struct { uint8_t external_detected; uint8_t gps_detected; uint8_t which_ref; uint8_t switch_pos; -} octoclock_state_t; +} octoclock_state_t __AVR_ALIGNED__; typedef struct { uint8_t num_wraps; uint8_t pos; -} gpsdo_cache_state_t; +} gpsdo_cache_state_t __AVR_ALIGNED__; typedef struct { uint32_t proto_ver; uint32_t sequence; uint8_t code; union { - uint16_t len; + uint16_t crc; gpsdo_cache_state_t state; uint16_t poolsize; uint16_t addr; }; uint8_t data[256]; -} octoclock_packet_t; + uint16_t len; +} octoclock_packet_t __AVR_ALIGNED__; +#ifndef AVR #pragma pack(pop) +#endif #ifdef __cplusplus } diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h new file mode 100644 index 000000000..20eba43cc --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex.h @@ -0,0 +1,191 @@ +/* + * kk_ihex.h: A simple library for reading and writing the Intel HEX + * or IHEX format. Intended mainly for embedded systems, and thus + * somewhat optimised for size at the expense of error handling and + * generality. + * + * USAGE + * ----- + * + * The library has been split into read and write parts, which use a + * common data structure (`struct ihex_state`), but each can be used + * independently. Include the header `kk_ihex_read.h` for reading, and/or + * the header `kk_ihex_write.h` for writing (and link with their respective + * object files). Both can be used simultaneously - this header defines + * the shared data structures and definitions. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See the header + * `kk_ihex_read.h` for details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * WRITING BINARY DATA AS INTEL HEX + * -------------------------------- + * + * In order to write out data, the `ihex_write_at_address` or + * `ihex_write_at_segment` functions are used to set the data location, + * and then the binary bytes are written with `ihex_write_byte` and/or + * `ihex_write_bytes`. The writing functions will then call the function + * `ihex_flush_buffer` whenever the internal write buffer needs to be + * cleared - it is up to the caller to provide an implementation of + * `ihex_flush_buffer` to do the actual writing. See the header + * `kk_ihex_write.h` for details and an example implementation. + * + * See the declaration further down for an example implementation. + * + * The sequence to write data in IHEX format is: + * struct ihex_state ihex; + * ihex_init(&ihex); + * ihex_write_at_address(&ihex, 0); + * ihex_write_bytes(&ihex, my_data, length_of_my_data); + * ihex_end_write(&ihex); + * + * For outputs larger than 64KiB, 32-bit linear addresses are output. Normally + * the initial linear extended address record of zero is NOT written - it can + * be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before + * writing the first byte. + * + * Gaps in the data may be created by calling `ihex_write_at_address` with the + * new starting address without calling `ihex_end_write` in between. + * + * + * The same `struct ihex_state` may be used either for reading or writing, + * but NOT both at the same time. Furthermore, a global output buffer is + * used for writing, i.e., multiple threads must not write simultaneously + * (but multiple writes may be interleaved). + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. For write only there is no reason to define the + * maximum as greater than the line length you'll actually be writing, + * e.g., 32 or 16. + * + * If the write functionality is only occasionally used, you can provide + * your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER` + * and providing a `char *ihex_write_buffer` which points to valid storage + * for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first + * call to any IHEX write function to until after the last. + * + * If you are doing both reading and writing, you can define the maximum + * output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will + * decrease the write buffer size, but `struct ihex_state` will still + * use the larger `IHEX_LINE_MAX_LENGTH` for its data storage. + * + * You can also save a few additional bytes by disabling support for + * segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the + * read and write modules need to be build with the same option, as the + * resulting data structures will not be compatible otherwise. To be honest, + * this is a fairly pointless optimisation. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + */ + +#ifndef KK_IHEX_H +#define KK_IHEX_H + +#define KK_IHEX_VERSION "2015-08-10" + +#include <stdint.h> + +#ifdef IHEX_USE_STDBOOL +#include <stdbool.h> +typedef bool ihex_bool_t; +#else +typedef uint_fast8_t ihex_bool_t; +#endif + +typedef uint_least32_t ihex_address_t; +typedef uint_least16_t ihex_segment_t; +typedef int ihex_count_t; + +// Maximum number of data bytes per line (applies to both reading and +// writing!); specify 255 to support reading all possible lengths. Less +// can be used to limit memory footprint on embedded systems, e.g., +// most programs with IHEX output use 32. +#ifndef IHEX_LINE_MAX_LENGTH +#define IHEX_LINE_MAX_LENGTH 255 +#endif + +enum ihex_flags { + IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow +}; +typedef uint8_t ihex_flags_t; + +typedef struct ihex_state { + ihex_address_t address; +#ifndef IHEX_DISABLE_SEGMENTS + ihex_segment_t segment; +#endif + ihex_flags_t flags; + uint8_t line_length; + uint8_t length; + uint8_t data[IHEX_LINE_MAX_LENGTH + 1]; +} kk_ihex_t; + +enum ihex_record_type { + IHEX_DATA_RECORD, + IHEX_END_OF_FILE_RECORD, + IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD, + IHEX_START_SEGMENT_ADDRESS_RECORD, + IHEX_EXTENDED_LINEAR_ADDRESS_RECORD, + IHEX_START_LINEAR_ADDRESS_RECORD +}; +typedef uint8_t ihex_record_type_t; + +#ifndef IHEX_DISABLE_SEGMENTS + +// Resolve segmented address (if any). It is the author's recommendation that +// segmented addressing not be used (and indeed the write function of this +// library uses linear 32-bit addressing unless manually overridden). +// +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4)) +// +// Note that segmented addressing with the above macro is not strictly adherent +// to the IHEX specification, which mandates that the lowest 16 bits of the +// address and the index of the data byte must be added modulo 64K (i.e., +// at 16 bits precision with wraparound) and the segment address only added +// afterwards. +// +// To implement fully correct segmented addressing, compute the address +// of _each byte_ with its index in `data` as follows: +// +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4)) + +#else // IHEX_DISABLE_SEGMENTS: + +#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address) +#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index)) + +#endif + +// The newline string (appended to every output line, e.g., "\r\n") +#ifndef IHEX_NEWLINE_STRING +#define IHEX_NEWLINE_STRING "\n" +#endif + +// See kk_ihex_read.h and kk_ihex_write.h for function declarations! + +#endif // !KK_IHEX_H diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt new file mode 100644 index 000000000..530f413e3 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Kimmo Kulovesi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.c b/host/lib/usrp_clock/octoclock/kk_ihex_read.c new file mode 100644 index 000000000..964cdd165 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.c @@ -0,0 +1,261 @@ +/* + * kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format. + * + * See the header `kk_ihex.h` for instructions. + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#include "kk_ihex_read.h" + +#include <stdio.h> +#include <stdlib.h> + +#define IHEX_START ':' + +#define AUTODETECT_ADDRESS (~0UL) + +#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U) + +enum ihex_read_state { + READ_WAIT_FOR_START = 0, + READ_COUNT_HIGH = 1, + READ_COUNT_LOW, + READ_ADDRESS_MSB_HIGH, + READ_ADDRESS_MSB_LOW, + READ_ADDRESS_LSB_HIGH, + READ_ADDRESS_LSB_LOW, + READ_RECORD_TYPE_HIGH, + READ_RECORD_TYPE_LOW, + READ_DATA_HIGH, + READ_DATA_LOW +}; + +#define IHEX_READ_RECORD_TYPE_MASK 0x07 +#define IHEX_READ_STATE_MASK 0x78 +#define IHEX_READ_STATE_OFFSET 3 + +void +ihex_begin_read (struct ihex_state * const ihex) { + ihex->address = 0; +#ifndef IHEX_DISABLE_SEGMENTS + ihex->segment = 0; +#endif + ihex->flags = 0; + ihex->line_length = 0; + ihex->length = 0; +} + +void +ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) { + ihex_begin_read(ihex); + ihex->address = address; +} + +#ifndef IHEX_DISABLE_SEGMENTS +void +ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) { + ihex_begin_read(ihex); + ihex->segment = segment; +} +#endif + +void +ihex_end_read (struct ihex_state * const ihex, FILE* outfile) { + uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK; + uint_fast8_t sum; + if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) { + return; + } + { + // compute and validate checksum + const uint8_t * const eptr = ihex->data + sum; + const uint8_t *r = ihex->data; + sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU); + while (r != eptr) { + sum += *r++; + } + sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum + } + if (ihex_data_read(ihex, type, sum, outfile)) { + if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) { + ihex->address &= 0xFFFFU; + ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) | + (((ihex_address_t) ihex->data[1]) << 16); +#ifndef IHEX_DISABLE_SEGMENTS + } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) { + ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]); +#endif + } + } + ihex->length = 0; + ihex->flags = 0; +} + +void +ihex_read_byte (struct ihex_state * const ihex, const char byte, FILE* outfile) { + uint_fast8_t b = (uint_fast8_t) byte; + uint_fast8_t len = ihex->length; + uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK); + ihex->flags ^= state; // turn off the old state + state >>= IHEX_READ_STATE_OFFSET; + + if (b >= '0' && b <= '9') { + b -= '0'; + } else if (b >= 'A' && b <= 'F') { + b -= 'A' - 10; + } else if (b >= 'a' && b <= 'f') { + b -= 'a' - 10; + } else if (b == IHEX_START) { + // sync to a new record at any state + state = READ_COUNT_HIGH; + goto end_read; + } else { + // ignore unknown characters (e.g., extra whitespace) + goto save_read_state; + } + + if (!(++state & 1)) { + // high nybble, store temporarily at end of data: + b <<= 4; + ihex->data[len] = b; + } else { + // low nybble, combine with stored high nybble: + b = (ihex->data[len] |= b); + switch (state >> 1) { + default: + // remain in initial state while waiting for : + return; + case (READ_COUNT_LOW >> 1): + // data length + ihex->line_length = b; +#if IHEX_LINE_MAX_LENGTH < 255 + if (b > IHEX_LINE_MAX_LENGTH) { + ihex_end_read(ihex); + return; + } +#endif + break; + case (READ_ADDRESS_MSB_LOW >> 1): + // high byte of 16-bit address + ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address + ihex->address |= ((ihex_address_t) b) << 8U; + break; + case (READ_ADDRESS_LSB_LOW >> 1): + // low byte of 16-bit address + ihex->address |= (ihex_address_t) b; + break; + case (READ_RECORD_TYPE_LOW >> 1): + // record type + if (b & ~IHEX_READ_RECORD_TYPE_MASK) { + // skip unknown record types silently + return; + } + ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b; + break; + case (READ_DATA_LOW >> 1): + if (len < ihex->line_length) { + // data byte + ihex->length = len + 1; + state = READ_DATA_HIGH; + goto save_read_state; + } + // end of line (last "data" byte is checksum) + state = READ_WAIT_FOR_START; + end_read: + ihex_end_read(ihex, outfile); + } + } +save_read_state: + ihex->flags |= state << IHEX_READ_STATE_OFFSET; +} + +void +ihex_read_bytes (struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile) { + while (count > 0) { + ihex_read_byte(ihex, *data++, outfile); + --count; + } +} + +ihex_bool_t +ihex_data_read (struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t error, + FILE* outfile) { + unsigned long line_number = 1L; + unsigned long file_position = 0L; + unsigned long address_offset = 0L; + bool debug_enabled = false; + + if (error) { + (void) fprintf(stderr, "Checksum error on line %lu\n", line_number); + return false; + } + if ((error = (ihex->length < ihex->line_length))) { + (void) fprintf(stderr, "Line length error on line %lu\n", line_number); + return false; + } + if (!outfile) { + (void) fprintf(stderr, "Excess data after end of file record\n"); + return false; + } + if (type == IHEX_DATA_RECORD) { + unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex); + if (address < address_offset) { + if (address_offset == AUTODETECT_ADDRESS) { + // autodetect initial address + address_offset = address; + if (debug_enabled) { + (void) fprintf(stderr, "Address offset: 0x%lx\n", + address_offset); + } + } else { + (void) fprintf(stderr, "Address underflow on line %lu\n", + line_number); + return false; + } + } + address -= address_offset; + if (address != file_position) { + if (debug_enabled) { + (void) fprintf(stderr, + "Seeking from 0x%lx to 0x%lx on line %lu\n", + file_position, address, line_number); + } + if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) { + if (file_position < address) { + // "seek" forward in stdout by writing NUL bytes + do { + (void) fputc('\0', outfile); + } while (++file_position < address); + } else { + perror("fseek"); + return false; + } + } + file_position = address; + } + if (!fwrite(ihex->data, ihex->length, 1, outfile)) { + perror("fwrite"); + return false; + } + file_position += ihex->length; + } else if (type == IHEX_END_OF_FILE_RECORD) { + if (debug_enabled) { + (void) fprintf(stderr, "%lu bytes written\n", file_position); + } + if (outfile != stdout) { + (void) fclose(outfile); + } + outfile = NULL; + } + return true; +} diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.h b/host/lib/usrp_clock/octoclock/kk_ihex_read.h new file mode 100644 index 000000000..5e210fddb --- /dev/null +++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.h @@ -0,0 +1,119 @@ +/* + * kk_ihex_read.h: A simple library for reading Intel HEX data. See + * the accompanying kk_ihex_write.h for IHEX write support. + * + * + * READING INTEL HEX DATA + * ---------------------- + * + * To read data in the Intel HEX format, you must perform the actual reading + * of bytes using other means (e.g., stdio). The bytes read must then be + * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions + * will then call `ihex_data_read`, at which stage the `struct ihex_state` + * structure will contain the data along with its address. See below for + * details and example implementation of `ihex_data_read`. + * + * The sequence to read data in IHEX format is: + * struct ihex_state ihex; + * ihex_begin_read(&ihex); + * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes); + * ihex_end_read(&ihex); + * + * + * CONSERVING MEMORY + * ----------------- + * + * For memory-critical use, you can save additional memory by defining + * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that + * this limit affects both reading and writing, so the resulting library + * will be unable to read lines with more than this number of data bytes. + * That said, I haven't encountered any IHEX files with more than 32 + * data bytes per line. + * + * + * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/ + * Provided with absolutely no warranty, use at your own risk only. + * Use and distribute freely, mark modified copies as such. + * + * Modifications Copyright (c) 2015 National Instruments Corp. + */ + +#ifndef KK_IHEX_READ_H +#define KK_IHEX_READ_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "kk_ihex.h" + +#include <stdio.h> + +// Begin reading at address 0 +void ihex_begin_read(struct ihex_state * const ihex); + +// Begin reading at `address` (the lowest 16 bits of which will be ignored); +// this is required only if the high bytes of the 32-bit starting address +// are not specified in the input data and they are non-zero +void ihex_read_at_address(struct ihex_state *ihex, + ihex_address_t address); + +// Read a single character +void ihex_read_byte(struct ihex_state *ihex, char chr, FILE* outfile); + +// Read `count` bytes from `data` +void ihex_read_bytes(struct ihex_state * ihex, + const char * data, + ihex_count_t count, + FILE* outfile); + +// End reading (may call `ihex_data_read` if there is data waiting) +void ihex_end_read(struct ihex_state *ihex, FILE* outfile); + +// Called when a complete line has been read, the record type of which is +// passed as `type`. The `ihex` structure will have its fields `data`, +// `line_length`, `address`, and `segment` set appropriately. In case +// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an +// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not +// yet parsed - it will be parsed into the `address` or `segment` field +// only if `ihex_data_read` returns `true`. This allows manual handling +// of extended addresses by parsing the `ihex->data` bytes. +// +// Possible error cases include checksum mismatch (which is indicated +// as an argument), and excessive line length (in case this has been +// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated +// by `line_length` greater than `length`. Unknown record types and +// other erroneous data is usually silently ignored by this minimalistic +// parser. (It is recommended to compute a hash over the complete data +// once received and verify that against the source.) +// +// Example implementation: +// +// ihex_bool_t ihex_data_read(struct ihex_state *ihex, +// ihex_record_type_t type, +// ihex_bool_t error) { +// error = error || (ihex->length < ihex->line_length); +// if (type == IHEX_DATA_RECORD && !error) { +// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET); +// (void) fwrite(ihex->data, 1, ihex->length, outfile); +// } else if (type == IHEX_END_OF_FILE_RECORD) { +// (void) fclose(outfile); +// } +// return !error; +// } +// +ihex_bool_t ihex_data_read(struct ihex_state *ihex, + ihex_record_type_t type, + ihex_bool_t checksum_mismatch, + FILE* outfile); + +// Begin reading at `segment`; this is required only if the initial segment +// is not specified in the input data and it is non-zero. +// +#ifndef IHEX_DISABLE_SEGMENTS +void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment); +#endif + +#ifdef __cplusplus +} +#endif +#endif // !KK_IHEX_READ_H diff --git a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp index 93c317191..49d1a0442 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // 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 @@ -19,6 +19,7 @@ #include <uhd/usrp_clock/octoclock_eeprom.hpp> #include <uhd/transport/udp_simple.hpp> #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/utils/byteswap.hpp> #include <boost/assign/list_of.hpp> @@ -37,26 +38,6 @@ using namespace uhd::usrp_clock; using namespace uhd::transport; /*********************************************************************** - * Utility functions - **********************************************************************/ - -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ - std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ - std::string out; - BOOST_FOREACH(boost::uint8_t byte, bytes){ - if (byte < 32 or byte > 127) return out; - out += byte; - } - return out; -} - -/*********************************************************************** * Implementation **********************************************************************/ void octoclock_eeprom_t::_load(){ diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp new file mode 100644 index 000000000..8b47da7e5 --- /dev/null +++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp @@ -0,0 +1,382 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "octoclock_impl.hpp" +#include "common.h" +#include "kk_ihex_read.h" + +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include <boost/cstdint.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread.hpp> + +#include <cstdio> +#include <cstring> +#include <fstream> +#include <iostream> +#include <string> + +namespace fs = boost::filesystem; +using namespace uhd; +using namespace uhd::usrp_clock; +using namespace uhd::transport; + +#define OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES (1024*120) // Last 8 MB are for bootloader +#define OCTOCLOCK_BLOCK_SIZE 256 + +/* + * OctoClock burn session + */ +typedef struct { + bool found; + uhd::device_addr_t dev_addr; + std::string given_filepath; + std::string actual_filepath; // If using a .hex, this is the converted .bin + bool from_hex; + boost::uint32_t size; + boost::uint16_t crc; + boost::uint16_t num_blocks; + udp_simple::sptr ctrl_xport; + udp_simple::sptr fw_xport; + boost::uint8_t data_in[udp_simple::mtu]; +} octoclock_session_t; + +static void octoclock_calculate_crc(octoclock_session_t &session){ + std::ifstream ifile(session.actual_filepath.c_str()); + boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES]; + ifile.read((char*)temp_image, session.size); + + session.crc = 0xFFFF; + for(size_t i = 0; i < session.size; i++){ + session.crc ^= temp_image[i]; + for(boost::uint8_t j = 0; j < 8; ++j){ + if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001; + else session.crc = (session.crc >> 1); + } + } + + ifile.close(); +} + +static void octoclock_convert_ihex(octoclock_session_t &session){ + struct ihex_state ihex; + ihex_count_t count; + char buf[256]; + FILE* infile = fopen(session.given_filepath.c_str(), "r"); + FILE* outfile = fopen(session.actual_filepath.c_str(), "w"); + uint64_t line_number = 1; + + ihex_begin_read(&ihex); + while(fgets(buf, 256, infile)){ + count = ihex_count_t(strlen(buf)); + ihex_read_bytes(&ihex, buf, count, outfile); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex, outfile); // Closes outfile + + (void)fclose(infile); +} + +static void octoclock_validate_firmware_image(octoclock_session_t &session){ + if(not fs::exists(session.given_filepath)){ + throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"") + % session.given_filepath)); + } + + std::string extension = fs::extension(session.given_filepath); + if(extension == ".bin"){ + session.actual_filepath = session.given_filepath; + session.from_hex = false; + } + else if(extension == ".hex"){ + session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) / + str(boost::format("octoclock_fw_%d.bin") + % time_spec_t::get_system_time().get_full_secs()) + ).string(); + + octoclock_convert_ihex(session); + session.from_hex = true; + } + else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin."))); + + session.size = fs::file_size(session.actual_filepath); + if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){ + throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d") + % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES)); + } + + session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1) + : (session.size / OCTOCLOCK_BLOCK_SIZE); + + octoclock_calculate_crc(session); +} + +static void octoclock_setup_session(octoclock_session_t &session, + const uhd::device_addr_t &args, + const std::string &filepath){ + + // See if we can find an OctoClock with the given args + device_addrs_t devs = octoclock_find(args); + if(devs.size() == 0){ + session.found = false; + return; + } + else if(devs.size() > 1){ + std::string err_msg = "Could not resolve given args to a single OctoClock device.\n" + "Applicable devices:\n"; + + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + std::string name = (dev["type"] == "octoclock") ? str(boost::format("OctoClock r%d") + % dev.get("revision","4")) + : "OctoClock Bootloader"; + err_msg += str(boost::format(" * %s (addr=%s)\n") + % name + % dev.get("addr")); + } + + err_msg += "\nSpecify one of these devices with the given args to load an image onto it."; + + throw uhd::runtime_error(err_msg); + } + + session.dev_addr = devs[0]; + + // If no filepath is given, use the default + if(filepath == ""){ + session.given_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex") + % session.dev_addr.get("revision","4") + )); + } + else session.given_filepath = filepath; + + octoclock_validate_firmware_image(session); + + session.ctrl_xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT)); + session.fw_xport = udp_simple::make_connected(session.dev_addr["addr"], + BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT)); +} + +static void octoclock_reset_into_bootloader(octoclock_session_t &session){ + + // Already in bootloader + if(session.dev_addr["type"] == "octoclock-bootloader") + return; + + octoclock_packet_t pkt_out; + pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len; + + std::cout << " -- Resetting into bootloader..." << std::flush; + UHD_OCTOCLOCK_SEND_AND_RECV(session.ctrl_xport, RESET_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){ + + // Make sure this device is now in its bootloader + boost::this_thread::sleep(boost::posix_time::milliseconds(500)); + uhd::device_addrs_t octoclocks = uhd::device::find( + uhd::device_addr_t(str(boost::format("addr=%s") + % session.dev_addr["addr"] + ))); + if(octoclocks.size() == 0){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } + else if(octoclocks[0]["type"] != "octoclock-bootloader"){ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } + else{ + std::cout << "successful." << std::endl; + session.dev_addr = octoclocks[0]; + } + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to reset OctoClock."); + } +} + +static void octoclock_burn(octoclock_session_t &session){ + + // Make sure we're in the bootloader for this + octoclock_reset_into_bootloader(session); + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + + // Tell OctoClock to prepare for burn + pkt_out.len = htonx<boost::uint16_t>(session.size); + size_t len = 0; + std::cout << " -- Preparing OctoClock for firmware load..." << std::flush; + pkt_out.len = session.size; + pkt_out.crc = session.crc; + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){ + std::cout << "successful." << std::endl; + } + else{ + std::cout << "failed." << std::endl; + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to prepare OctoClock for firmware load."); + } + + // Start burning + std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); + for(size_t i = 0; i < session.num_blocks; i++){ + pkt_out.sequence++; + pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE; + + std::cout << str(boost::format("\r -- Loading firmware: %d%% (%d/%d blocks)") + % int((double(i)/double(session.num_blocks))*100) + % i % session.num_blocks) + << std::flush; + + memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE); + image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE); + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in); + if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){ + image.close(); + std::cout << std::endl; + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to load firmware."); + } + } + + std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)") + % session.num_blocks % session.num_blocks) + << std::endl; + image.close(); +} + +static void octoclock_verify(octoclock_session_t &session){ + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len = 0; + + std::ifstream image(session.actual_filepath.c_str(), std::ios::binary); + boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE]; + boost::uint16_t cmp_len = 0; + for(size_t i = 0; i < session.num_blocks; i++){ + pkt_out.sequence++; + pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE; + + std::cout << str(boost::format("\r -- Verifying firmware load: %d%% (%d/%d blocks)") + % int((double(i)/double(session.num_blocks))*100) + % i % session.num_blocks) + << std::flush; + + memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE); + image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE); + cmp_len = image.gcount(); + + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){ + if(memcmp(pkt_in->data, image_part, cmp_len)){ + std::cout << std::endl; + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to verify OctoClock firmware."); + } + } + else{ + std::cout << std::endl; + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + throw uhd::runtime_error("Failed to verify OctoClock firmware."); + } + } + + image.close(); + if(session.from_hex){ + fs::remove(session.actual_filepath); + } + std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)") + % session.num_blocks % session.num_blocks) + << std::endl; +} + +static void octoclock_finalize(octoclock_session_t &session){ + + octoclock_packet_t pkt_out; + pkt_out.sequence = htonx<boost::uint32_t>(std::rand()); + const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in); + size_t len = 0; + + std::cout << " -- Finalizing firmware load..." << std::flush; + UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FINALIZE_BURNING_CMD, pkt_out, len, session.data_in); + if(UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){ + std::cout << "successful." << std::endl; + } + else{ + std::cout << "failed." << std::endl; + throw uhd::runtime_error("Failed to finalize OctoClock firmware load."); + } +} + +bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loader_args){ + octoclock_session_t session; + octoclock_setup_session(session, + image_loader_args.args, + image_loader_args.firmware_path + ); + if(!session.found or !image_loader_args.load_firmware) return false; + + std::cout << boost::format("Unit: OctoClock (%s)") + % session.dev_addr["addr"] + << std::endl; + std::cout << "Firmware: " << session.given_filepath << std::endl; + + octoclock_burn(session); + octoclock_verify(session); + octoclock_finalize(session); + + return true; +} + +UHD_STATIC_BLOCK(register_octoclock_image_loader){ + std::string recovery_instructions = "Aborting. Your OctoClock firmware is now corrupt. The bootloader\n" + "is functional, but the device will not have functional clock distribution.\n" + "Run this utility again to restore functionality or refer to:\n\n" + "http://files.ettus.com/manual/page_octoclock.html\n\n" + "for alternative setups."; + + image_loader::register_image_loader("octoclock", + octoclock_image_loader, + recovery_instructions); +} diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp index b98d95725..ef1bc8ca0 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp @@ -357,14 +357,14 @@ void octoclock_impl::_get_state(const std::string &oc){ } uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of - (NO_REF, "none") + (NO_REF, "none") (INTERNAL, "internal") (EXTERNAL, "external") ; uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of - (UP, "Prefer internal") - (DOWN, "Prefer external") + (PREFER_INTERNAL, "Prefer internal") + (PREFER_EXTERNAL, "Prefer external") ; sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){ @@ -410,7 +410,7 @@ boost::uint32_t octoclock_impl::_get_time(const std::string &oc){ } std::string octoclock_impl::_get_images_help_message(const std::string &addr){ - const std::string image_name = "octoclock_r4_fw.bin"; + const std::string image_name = "octoclock_r4_fw.hex"; //Check to see if image is in default location std::string image_location; diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp index ab45cd5f0..453e75ec5 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp +++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp @@ -31,6 +31,8 @@ #include "common.h" +uhd::device_addrs_t octoclock_find(const uhd::device_addr_t &hint); + /*! * OctoClock implementation guts */ diff --git a/host/lib/usrp_clock/usrp_clock_c.cpp b/host/lib/usrp_clock/usrp_clock_c.cpp new file mode 100644 index 000000000..220112f37 --- /dev/null +++ b/host/lib/usrp_clock/usrp_clock_c.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2015 Ettus Research LLC + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* C-Interface for multi_usrp_clock */ + +#include <uhd/utils/static.hpp> +#include <uhd/usrp_clock/multi_usrp_clock.hpp> + +#include <uhd/usrp_clock/usrp_clock.h> + +#include <boost/foreach.hpp> +#include <boost/thread/mutex.hpp> + +#include <string.h> +#include <map> + +/**************************************************************************** + * Registry / Pointer Management + ***************************************************************************/ +/* Public structs */ +struct uhd_usrp_clock { + size_t usrp_clock_index; + std::string last_error; +}; + +/* Not public: We use this for our internal registry */ +struct usrp_clock_ptr { + uhd::usrp_clock::multi_usrp_clock::sptr ptr; + static size_t usrp_clock_counter; +}; +size_t usrp_clock_ptr::usrp_clock_counter = 0; +typedef struct usrp_clock_ptr usrp_clock_ptr; +/* Prefer map, because the list can be discontiguous */ +typedef std::map<size_t, usrp_clock_ptr> usrp_clock_ptrs; + +UHD_SINGLETON_FCN(usrp_clock_ptrs, get_usrp_clock_ptrs); +/* Shortcut for accessing the underlying USRP clock sptr from a uhd_usrp_clock_handle* */ +#define USRP_CLOCK(h_ptr) (get_usrp_clock_ptrs()[h_ptr->usrp_clock_index].ptr) + +/**************************************************************************** + * Generate / Destroy API calls + ***************************************************************************/ +static boost::mutex _usrp_clock_find_mutex; +uhd_error uhd_usrp_clock_find( + const char* args, + uhd_string_vector_t *devices_out +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_find_mutex); + + uhd::device_addrs_t devs = uhd::device::find(std::string(args), uhd::device::CLOCK); + devices_out->string_vector_cpp.clear(); + BOOST_FOREACH(const uhd::device_addr_t &dev, devs){ + devices_out->string_vector_cpp.push_back(dev.to_string()); + } + ) +} + +static boost::mutex _usrp_clock_make_mutex; +uhd_error uhd_usrp_clock_make( + uhd_usrp_clock_handle *h, + const char *args +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_make_mutex); + + size_t usrp_clock_count = usrp_clock_ptr::usrp_clock_counter; + usrp_clock_ptr::usrp_clock_counter++; + + // Initialize USRP Clock + uhd::device_addr_t device_addr(args); + usrp_clock_ptr P; + P.ptr = uhd::usrp_clock::multi_usrp_clock::make(device_addr); + + // Dump into registry + get_usrp_clock_ptrs()[usrp_clock_count] = P; + + // Update handle + (*h) = new uhd_usrp_clock; + (*h)->usrp_clock_index = usrp_clock_count; + ) +} + +static boost::mutex _usrp_clock_free_mutex; +uhd_error uhd_usrp_clock_free( + uhd_usrp_clock_handle *h +){ + UHD_SAFE_C( + boost::mutex::scoped_lock lock(_usrp_clock_free_mutex); + + if(!get_usrp_clock_ptrs().count((*h)->usrp_clock_index)){ + return UHD_ERROR_INVALID_DEVICE; + } + + get_usrp_clock_ptrs().erase((*h)->usrp_clock_index); + delete *h; + *h = NULL; + ) +} + +uhd_error uhd_usrp_clock_last_error( + uhd_usrp_clock_handle h, + char* error_out, + size_t strbuffer_len +){ + UHD_SAFE_C( + memset(error_out, '\0', strbuffer_len); + strncpy(error_out, h->last_error.c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_clock_get_pp_string( + uhd_usrp_clock_handle h, + char* pp_string_out, + size_t strbuffer_len +){ + UHD_SAFE_C_SAVE_ERROR(h, + memset(pp_string_out, '\0', strbuffer_len); + strncpy(pp_string_out, USRP_CLOCK(h)->get_pp_string().c_str(), strbuffer_len); + ) +} + +uhd_error uhd_usrp_clock_get_num_boards( + uhd_usrp_clock_handle h, + size_t *num_boards_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *num_boards_out = USRP_CLOCK(h)->get_num_boards(); + ) +} + +uhd_error uhd_usrp_clock_get_time( + uhd_usrp_clock_handle h, + size_t board, + uint32_t *clock_time_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + *clock_time_out = USRP_CLOCK(h)->get_time(board); + ) +} + +uhd_error uhd_usrp_clock_get_sensor( + uhd_usrp_clock_handle h, + const char* name, + size_t board, + uhd_sensor_value_handle *sensor_value_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + delete (*sensor_value_out)->sensor_value_cpp; + (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP_CLOCK(h)->get_sensor(name, board)); + ) +} + +uhd_error uhd_usrp_clock_get_sensor_names( + uhd_usrp_clock_handle h, + size_t board, + uhd_string_vector_handle *sensor_names_out +){ + UHD_SAFE_C_SAVE_ERROR(h, + (*sensor_names_out)->string_vector_cpp = USRP_CLOCK(h)->get_sensor_names(board); + ) +} diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 369920ac1..c5c975dfa 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -141,3 +141,9 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/tasks.cpp ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp ) + +IF(ENABLE_C_API) + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority_c.cpp + ) +ENDIF(ENABLE_C_API) diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index f29318ddd..eb9e69a49 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2015 Ettus Research LLC // // 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 @@ -111,6 +111,7 @@ static std::vector<std::string> get_env_paths(const std::string &var_name){ return paths; } +#ifndef UHD_PLATFORM_WIN32 /*! Expand a tilde character to the $HOME path. * * The path passed to this function must start with the tilde character in order @@ -132,6 +133,7 @@ static std::string expand_home_directory(std::string path) { return path; } +#endif /*********************************************************************** * Implement the functions in paths.hpp @@ -239,7 +241,7 @@ std::string _get_images_path_from_registry(const std::string& registry_key_path) //Get a handle to the key location HKEY hkey_location; - if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), NULL, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) + if (RegOpenKeyExA(hkey_parent, reg_path.c_str(), 0, KEY_QUERY_VALUE, &hkey_location) != ERROR_SUCCESS) return std::string(); //Query key value @@ -259,7 +261,7 @@ std::string _get_images_path_from_registry(const std::string& registry_key_path) } #endif /*UHD_PLATFORM_WIN32*/ -std::string uhd::get_images_dir(const std::string search_paths) { +std::string uhd::get_images_dir(const std::string &search_paths) { /* This function will check for the existence of directories in this * order: @@ -330,7 +332,7 @@ std::string uhd::get_images_dir(const std::string search_paths) { } } -std::string uhd::find_image_path(const std::string &image_name, const std::string search_paths){ +std::string uhd::find_image_path(const std::string &image_name, const std::string &search_paths){ /* If a path was provided on the command-line or as a hint from the caller, * we default to that. */ if (fs::exists(image_name)){ @@ -362,15 +364,15 @@ std::string uhd::find_image_path(const std::string &image_name, const std::strin + uhd::print_utility_error("uhd_images_downloader.py")); } -std::string uhd::find_utility(std::string name) { +std::string uhd::find_utility(const std::string &name) { return fs::path(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / name) .string(); } -std::string uhd::print_utility_error(std::string name){ +std::string uhd::print_utility_error(const std::string &name, const std::string &args){ #ifdef UHD_PLATFORM_WIN32 - return "As an Administrator, please run:\n\n\"" + find_utility(name) + "\""; + return "As an Administrator, please run:\n\n\"" + find_utility(name) + args + "\""; #else - return "Please run:\n\n \"" + find_utility(name) + "\""; + return "Please run:\n\n \"" + find_utility(name) + (args.empty() ? "" : (" " + args)) + "\""; #endif } diff --git a/host/lib/utils/platform.cpp b/host/lib/utils/platform.cpp index e2f92039e..a9cef663b 100644 --- a/host/lib/utils/platform.cpp +++ b/host/lib/utils/platform.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2014 Ettus Research LLC // // 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 @@ -19,7 +19,7 @@ #include <uhd/config.hpp> #include <boost/functional/hash.hpp> #ifdef UHD_PLATFORM_WIN32 -#include <Windows.h> +#include <windows.h> #else #include <unistd.h> #endif diff --git a/host/lib/utils/thread_priority.cpp b/host/lib/utils/thread_priority.cpp index 7c3faa37a..98023c5aa 100644 --- a/host/lib/utils/thread_priority.cpp +++ b/host/lib/utils/thread_priority.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,2015 Ettus Research LLC // // 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 @@ -74,7 +74,7 @@ static void check_priority_range(float priority){ #ifdef HAVE_WIN_SETTHREADPRIORITY #include <windows.h> - void uhd::set_thread_priority(float priority, bool realtime){ + void uhd::set_thread_priority(float priority, UHD_UNUSED(bool realtime)){ check_priority_range(priority); /* diff --git a/host/lib/utils/thread_priority_c.cpp b/host/lib/utils/thread_priority_c.cpp new file mode 100644 index 000000000..fe019e51d --- /dev/null +++ b/host/lib/utils/thread_priority_c.cpp @@ -0,0 +1,33 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/error.h> +#include <uhd/utils/thread_priority.h> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <iostream> + +uhd_error uhd_set_thread_priority( + float priority, + bool realtime +){ + UHD_SAFE_C( + uhd::set_thread_priority(priority, realtime); + ) +} diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 62544b69b..8b12c961f 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2015 Ettus Research LLC # # 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 @@ -27,16 +27,19 @@ SET(test_sources addr_test.cpp buffer_test.cpp byteswap_test.cpp - convert_test.cpp cast_test.cpp + chdr_test.cpp + convert_test.cpp dict_test.cpp error_test.cpp fp_compare_delta_test.cpp fp_compare_epsilon_test.cpp gain_group_test.cpp + math_test.cpp msg_test.cpp property_test.cpp ranges_test.cpp + sid_t_test.cpp sph_recv_test.cpp sph_send_test.cpp subdev_spec_test.cpp @@ -47,14 +50,22 @@ SET(test_sources #turn each test cpp file into an executable with an int main() function ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) -SET(UHD_TEST_TARGET_DEPS uhd) -SET(UHD_TEST_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) +IF(ENABLE_C_API) + LIST(APPEND test_sources + eeprom_c_test.c + error_c_test.cpp + ranges_c_test.c + sensors_c_test.c + string_vector_c_test.c + subdev_spec_c_test.c + ) +ENDIF(ENABLE_C_API) #for each source: build an executable, register it as a test FOREACH(test_source ${test_sources}) GET_FILENAME_COMPONENT(test_name ${test_source} NAME_WE) ADD_EXECUTABLE(${test_name} ${test_source}) - TARGET_LINK_LIBRARIES(${test_name} uhd) + TARGET_LINK_LIBRARIES(${test_name} uhd ${Boost_LIBRARIES}) UHD_ADD_TEST(${test_name} ${test_name}) UHD_INSTALL(TARGETS ${test_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) ENDFOREACH(test_source) diff --git a/host/tests/addr_test.cpp b/host/tests/addr_test.cpp index cea2f224c..61bb6d049 100644 --- a/host/tests/addr_test.cpp +++ b/host/tests/addr_test.cpp @@ -66,6 +66,13 @@ BOOST_AUTO_TEST_CASE(test_device_addr){ old_dev_addr_vals.begin(), old_dev_addr_vals.end(), new_dev_addr_vals.begin(), new_dev_addr_vals.end() ); + + uhd::device_addr_t dev_addr_lhs1("key1=val1,key2=val2"); + dev_addr_lhs1.update(uhd::device_addr_t("key2=val2x,key3=val3"), false); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key1"], "val1"); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key2"], "val2x"); + BOOST_CHECK_EQUAL(dev_addr_lhs1["key3"], "val3"); + std::cout << "Merged: " << dev_addr_lhs1.to_string() << std::endl; } BOOST_AUTO_TEST_CASE(test_dboard_id){ diff --git a/host/tests/chdr_test.cpp b/host/tests/chdr_test.cpp new file mode 100644 index 000000000..f48073a09 --- /dev/null +++ b/host/tests/chdr_test.cpp @@ -0,0 +1,144 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/chdr.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/format.hpp> +#include <cstdlib> +#include <iostream> + +using namespace uhd::transport::vrt; + +static void pack_and_unpack( + if_packet_info_t &if_packet_info_in +){ + // Temp buffer for packed packet + boost::uint32_t packet_buff[2048] = {0}; + + // Check input (must not be lazy) + BOOST_REQUIRE( + (if_packet_info_in.num_payload_words32 == 0 and if_packet_info_in.num_payload_bytes == 0) + or + (if_packet_info_in.num_payload_words32 != 0 and if_packet_info_in.num_payload_bytes != 0) + ); + if (if_packet_info_in.num_payload_words32) { + BOOST_REQUIRE(if_packet_info_in.num_payload_bytes <= 4 * if_packet_info_in.num_payload_words32); + BOOST_REQUIRE(if_packet_info_in.num_payload_bytes > 4*(if_packet_info_in.num_payload_words32-1)); + } + + //pack metadata into a vrt header + chdr::if_hdr_pack_be( + packet_buff, if_packet_info_in + ); + std::cout << std::endl; + boost::uint32_t header_bits = (uhd::ntohx(packet_buff[0]) >> 28); + std::cout << boost::format("header bits = 0b%d%d%d%d") % ((header_bits & 8) > 0) + % ((header_bits & 4) > 0) + % ((header_bits & 2) > 0) + % ((header_bits & 1) > 0) << std::endl; + for (size_t i = 0; i < 5; i++) + { + std::cout << boost::format("packet_buff[%u] = 0x%08x") % i % uhd::ntohx(packet_buff[i]) << std::endl; + } + + if_packet_info_t if_packet_info_out; + // Must be set a-priori as per contract + if_packet_info_out.num_packet_words32 = if_packet_info_in.num_packet_words32; + + //unpack the vrt header back into metadata + chdr::if_hdr_unpack_be( + packet_buff, if_packet_info_out + ); + + //check the the unpacked metadata is the same + BOOST_CHECK_EQUAL(if_packet_info_in.packet_count, if_packet_info_out.packet_count); + BOOST_CHECK_EQUAL(if_packet_info_in.num_header_words32, if_packet_info_out.num_header_words32); + BOOST_CHECK_EQUAL(if_packet_info_in.num_payload_words32, if_packet_info_out.num_payload_words32); + BOOST_CHECK(if_packet_info_out.has_sid); + BOOST_CHECK_EQUAL(if_packet_info_in.sid, if_packet_info_out.sid); + BOOST_CHECK(if_packet_info_out.has_sid); + BOOST_CHECK_EQUAL(if_packet_info_in.has_tsf, if_packet_info_out.has_tsf); + if (if_packet_info_in.has_tsf and if_packet_info_out.has_tsf){ + BOOST_CHECK_EQUAL(if_packet_info_in.tsf, if_packet_info_out.tsf); + } +} + +BOOST_AUTO_TEST_CASE(test_with_chdr){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_DATA; + if_packet_info.eob = false; + if_packet_info.packet_count = 7; + if_packet_info.has_tsf = true; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 24; + if_packet_info.num_payload_bytes = 95; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_fc){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_FC; + if_packet_info.eob = false; + if_packet_info.packet_count = 19; + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_cmd){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_CMD; + if_packet_info.packet_count = 19; + if_packet_info.has_tsf = true; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_resp){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_RESP; + if_packet_info.packet_count = 123; + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + +BOOST_AUTO_TEST_CASE(test_with_chdr_err){ + if_packet_info_t if_packet_info; + if_packet_info.packet_type = if_packet_info_t::PACKET_TYPE_ERROR; + if_packet_info.packet_count = 1928; + if_packet_info.eob = false; + if_packet_info.error = false; // Needs to be set explicitly + if_packet_info.has_tsf = false; + if_packet_info.tsf = 0x1234567890ABCDEFull; + if_packet_info.sid = 0xAABBCCDD; + if_packet_info.num_payload_words32 = 4; + if_packet_info.num_payload_bytes = 16; + pack_and_unpack(if_packet_info); +} + diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp index 8ef1e74cc..d71d756dd 100644 --- a/host/tests/convert_test.cpp +++ b/host/tests/convert_test.cpp @@ -415,3 +415,43 @@ BOOST_AUTO_TEST_CASE(test_convert_types_sc16_and_sc8){ test_convert_types_sc16(nsamps, id, 256); } } + +/*********************************************************************** + * Test short conversion + **********************************************************************/ +static void test_convert_types_u8( + size_t nsamps, convert::id_type &id +){ + //fill the input samples + std::vector<boost::uint8_t> input(nsamps), output(nsamps); + //BOOST_FOREACH(boost::uint8_t &in, input) in = boost::uint8_t(std::rand() & 0xFF); + boost::uint32_t d = 48; + BOOST_FOREACH(boost::uint8_t &in, input) in = d++; + + //run the loopback and test + convert::id_type in_id = id; + convert::id_type out_id = id; + std::swap(out_id.input_format, out_id.output_format); + std::swap(out_id.num_inputs, out_id.num_outputs); + loopback(nsamps, in_id, out_id, input, output); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8){ + convert::id_type id; + id.input_format = "u8"; + id.num_inputs = 1; + id.num_outputs = 1; + + //try various lengths to test edge cases + id.output_format = "u8_item32_le"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_u8(nsamps, id); + } + + //try various lengths to test edge cases + id.output_format = "u8_item32_be"; + for (size_t nsamps = 1; nsamps < 16; nsamps++){ + test_convert_types_u8(nsamps, id); + } +} diff --git a/host/tests/dict_test.cpp b/host/tests/dict_test.cpp index 7b388d090..333aadbba 100644 --- a/host/tests/dict_test.cpp +++ b/host/tests/dict_test.cpp @@ -70,3 +70,28 @@ BOOST_AUTO_TEST_CASE(test_dict_pop){ BOOST_CHECK(d.keys()[0] == -1); BOOST_CHECK(d.keys()[1] == 1); } + +BOOST_AUTO_TEST_CASE(test_dict_update) +{ + uhd::dict<std::string, std::string> d1 = boost::assign::map_list_of + ("key1", "val1") + ("key2", "val2") + ; + uhd::dict<std::string, std::string> d2 = boost::assign::map_list_of + ("key2", "val2x") + ("key3", "val3") + ; + + d1.update(d2, false /* don't throw cause of conflict */); + BOOST_CHECK_EQUAL(d1["key1"], "val1"); + BOOST_CHECK_EQUAL(d1["key2"], "val2x"); + BOOST_CHECK_EQUAL(d1["key3"], "val3"); + + uhd::dict<std::string, std::string> d3 = boost::assign::map_list_of + ("key1", "val1") + ("key2", "val2") + ; + BOOST_CHECK_THROW(d3.update(d2), uhd::value_error); +} + + diff --git a/host/tests/eeprom_c_test.c b/host/tests/eeprom_c_test.c new file mode 100644 index 000000000..1b29dfd44 --- /dev/null +++ b/host/tests/eeprom_c_test.c @@ -0,0 +1,157 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define UHD_TEST_EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + fprintf(stderr, "Error occurred at %s:%d\n", __FILE__, (__LINE__-1)); \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +#define BUFFER_SIZE 1024 + +int main(){ + + // Variables + int return_code; + uhd_mboard_eeprom_handle mb_eeprom; + uhd_dboard_eeprom_handle db_eeprom; + int db_revision; + char str_buffer[BUFFER_SIZE]; + + return_code = EXIT_SUCCESS; + + /* + * Motherboard EEPROM test + */ + + // Create EEPROM handle + UHD_TEST_EXECUTE_OR_GOTO(end_of_test, + uhd_mboard_eeprom_make(&mb_eeprom) + ) + + // Set a value, retrieve it, and make sure it matches + UHD_TEST_EXECUTE_OR_GOTO(free_mboard_eeprom, + uhd_mboard_eeprom_set_value( + mb_eeprom, + "serial", + "F12345" + ) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_mboard_eeprom, + uhd_mboard_eeprom_get_value( + mb_eeprom, + "serial", + str_buffer, + BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "F12345")){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Mismatched EEPROM value: \"%s\" vs. \"F12345\"\n", + __FILE__, __LINE__, + str_buffer); + goto free_mboard_eeprom; + } + + /* + * Daughterboard EEPROM test + */ + + // Create EEPROM handle + UHD_TEST_EXECUTE_OR_GOTO(free_mboard_eeprom, + uhd_dboard_eeprom_make(&db_eeprom) + ) + + // Set the ID, retrieve it, and make sure it matches + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_set_id(db_eeprom, "0x0067") + ) + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_get_id( + db_eeprom, + str_buffer, + BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "0x0067")){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Mismatched daughterboard ID: \"%s\" vs. \"0x0067\"\n", + __FILE__, __LINE__, + str_buffer); + goto free_dboard_eeprom; + } + + // Set the serial, retrieve it, and make sure it matches + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_set_serial(db_eeprom, "F12345") + ) + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_get_serial( + db_eeprom, + str_buffer, + BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "F12345")){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Mismatched daughterboard serial: \"%s\" vs. \"F12345\"\n", + __FILE__, __LINE__, + str_buffer); + goto free_dboard_eeprom; + } + + // Set the revision, retrieve it, and make sure it matches + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_set_revision(db_eeprom, 4) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_dboard_eeprom, + uhd_dboard_eeprom_get_revision(db_eeprom, &db_revision) + ) + if(db_revision != 4){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Mismatched daughterboard revision: \"%d\" vs. 4\n", + __FILE__, __LINE__, db_revision); + goto free_dboard_eeprom; + } + + free_dboard_eeprom: + if(return_code){ + uhd_dboard_eeprom_last_error(db_eeprom, str_buffer, BUFFER_SIZE); + fprintf(stderr, "db_eeprom error: %s\n", str_buffer); + } + uhd_dboard_eeprom_free(&db_eeprom); + + free_mboard_eeprom: + if(return_code){ + uhd_mboard_eeprom_last_error(mb_eeprom, str_buffer, BUFFER_SIZE); + fprintf(stderr, "mb_eeprom error: %s\n", str_buffer); + } + uhd_mboard_eeprom_free(&mb_eeprom); + + end_of_test: + if(!return_code){ + printf("\nNo errors detected\n"); + } + return return_code; +} diff --git a/host/tests/error_c_test.cpp b/host/tests/error_c_test.cpp new file mode 100644 index 000000000..8eb90f3d4 --- /dev/null +++ b/host/tests/error_c_test.cpp @@ -0,0 +1,144 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/error.h> +#include <uhd/types/dict.hpp> + +#include <boost/test/unit_test.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign.hpp> +#include <boost/format.hpp> + +/* + * Test our conversions from exceptions to C-level UHD error codes. + * We use inline functions separate from the Boost test cases themselves + * to test our C++ macro, which returns the error code. + */ + +typedef struct { + std::string last_error; +} dummy_handle_t; + +template <typename error_type> +UHD_INLINE uhd_error throw_uhd_exception(dummy_handle_t *handle, const uhd::exception* except){ + UHD_SAFE_C_SAVE_ERROR(handle, + throw *dynamic_cast<const error_type*>(except); + ) +} + +UHD_INLINE uhd_error throw_boost_exception(dummy_handle_t *handle){ + UHD_SAFE_C_SAVE_ERROR(handle, + std::runtime_error except("This is a std::runtime_error, thrown by Boost."); + BOOST_THROW_EXCEPTION(except); + ) +} + +UHD_INLINE uhd_error throw_std_exception(dummy_handle_t *handle){ + UHD_SAFE_C_SAVE_ERROR(handle, + throw std::runtime_error("This is a std::runtime_error."); + ) +} + +UHD_INLINE uhd_error throw_unknown_exception(dummy_handle_t *handle){ + UHD_SAFE_C_SAVE_ERROR(handle, + throw 1; + ) +} + +// There are enough non-standard names that we can't just use a conversion function +static const uhd::dict<std::string, std::string> pretty_exception_names = + boost::assign::map_list_of + ("assertion_error", "AssertionError") + ("lookup_error", "LookupError") + ("index_error", "LookupError: IndexError") + ("key_error", "LookupError: KeyError") + ("type_error", "TypeError") + ("value_error", "ValueError") + ("runtime_error", "RuntimeError") + ("not_implemented_error", "RuntimeError: NotImplementedError") + ("usb_error", "RuntimeError: USBError 1") + ("environment_error", "EnvironmentError") + ("io_error", "EnvironmentError: IOError") + ("os_error", "EnvironmentError: OSError") + ("system_error", "SystemError") + ; + +#define UHD_TEST_CHECK_ERROR_CODE(cpp_exception_type, c_error_code) \ + expected_msg = str(boost::format("This is a uhd::%s.") % BOOST_STRINGIZE(cpp_exception_type)); \ + uhd::cpp_exception_type cpp_exception_type ## _foo(expected_msg); \ + error_code = throw_uhd_exception<uhd::cpp_exception_type>(&handle, &cpp_exception_type ## _foo); \ + BOOST_CHECK_EQUAL(error_code, c_error_code); \ + expected_msg = str(boost::format("%s: %s") \ + % pretty_exception_names.get(BOOST_STRINGIZE(cpp_exception_type)) \ + % expected_msg); \ + BOOST_CHECK_EQUAL(handle.last_error, expected_msg); \ + BOOST_CHECK_EQUAL(get_c_global_error_string(), expected_msg); + +// uhd::usb_error has a different constructor +#define UHD_TEST_CHECK_USB_ERROR_CODE() \ + expected_msg = "This is a uhd::usb_error."; \ + uhd::usb_error usb_error_foo(1, expected_msg); \ + error_code = throw_uhd_exception<uhd::usb_error>(&handle, &usb_error_foo); \ + BOOST_CHECK_EQUAL(error_code, UHD_ERROR_USB); \ + expected_msg = str(boost::format("%s: %s") \ + % pretty_exception_names.get("usb_error") \ + % expected_msg); \ + BOOST_CHECK_EQUAL(handle.last_error, expected_msg); \ + BOOST_CHECK_EQUAL(get_c_global_error_string(), expected_msg); + +BOOST_AUTO_TEST_CASE(test_uhd_exception){ + dummy_handle_t handle; + std::string expected_msg; + uhd_error error_code; + + UHD_TEST_CHECK_ERROR_CODE(assertion_error, UHD_ERROR_ASSERTION); + UHD_TEST_CHECK_ERROR_CODE(lookup_error, UHD_ERROR_LOOKUP); + UHD_TEST_CHECK_ERROR_CODE(index_error, UHD_ERROR_INDEX); + UHD_TEST_CHECK_ERROR_CODE(key_error, UHD_ERROR_KEY); + UHD_TEST_CHECK_ERROR_CODE(type_error, UHD_ERROR_TYPE); + UHD_TEST_CHECK_ERROR_CODE(value_error, UHD_ERROR_VALUE); + UHD_TEST_CHECK_ERROR_CODE(runtime_error, UHD_ERROR_RUNTIME); + UHD_TEST_CHECK_ERROR_CODE(not_implemented_error, UHD_ERROR_NOT_IMPLEMENTED); + UHD_TEST_CHECK_ERROR_CODE(io_error, UHD_ERROR_IO); + UHD_TEST_CHECK_ERROR_CODE(os_error, UHD_ERROR_OS); + UHD_TEST_CHECK_ERROR_CODE(system_error, UHD_ERROR_SYSTEM); + UHD_TEST_CHECK_USB_ERROR_CODE(); +} + +BOOST_AUTO_TEST_CASE(test_boost_exception){ + dummy_handle_t handle; + uhd_error error_code = throw_boost_exception(&handle); + + // Boost error message cannot be determined here, so just check code + BOOST_CHECK_EQUAL(error_code, UHD_ERROR_BOOSTEXCEPT); +} + +BOOST_AUTO_TEST_CASE(test_std_exception){ + dummy_handle_t handle; + uhd_error error_code = throw_std_exception(&handle); + + BOOST_CHECK_EQUAL(error_code, UHD_ERROR_STDEXCEPT); + BOOST_CHECK_EQUAL(handle.last_error, "This is a std::runtime_error."); +} + +BOOST_AUTO_TEST_CASE(test_unknown_exception){ + dummy_handle_t handle; + uhd_error error_code = throw_unknown_exception(&handle); + + BOOST_CHECK_EQUAL(error_code, UHD_ERROR_UNKNOWN); + BOOST_CHECK_EQUAL(handle.last_error, "Unrecognized exception caught."); +} diff --git a/host/tests/fp_compare_delta_test.cpp b/host/tests/fp_compare_delta_test.cpp index 0ac4e257d..36ff14756 100644 --- a/host/tests/fp_compare_delta_test.cpp +++ b/host/tests/fp_compare_delta_test.cpp @@ -239,12 +239,12 @@ BOOST_AUTO_TEST_CASE(double_greaterthanequals_operators) { BOOST_AUTO_TEST_CASE(frequency_compare_function) { - BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232, 6817333232)); - BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333233, 6817333232)); + BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232.0, 6817333232.0)); + BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333233.0, 6817333232.0)); BOOST_CHECK(uhd::math::frequencies_are_equal(6817333232.1, 6817333232.1)); BOOST_CHECK(!uhd::math::frequencies_are_equal(6817333232.5, 6817333232.6)); BOOST_CHECK(uhd::math::frequencies_are_equal(16.8173332321e9, 16.8173332321e9)); BOOST_CHECK(!uhd::math::frequencies_are_equal(16.8173332322e9, 16.8173332321e9)); BOOST_CHECK(!uhd::math::frequencies_are_equal(5.0, 4.0)); - BOOST_CHECK(uhd::math::frequencies_are_equal(48750000, 48749999.9946)); + BOOST_CHECK(uhd::math::frequencies_are_equal(48750000.0, 48749999.9946)); } diff --git a/host/utils/ihexcvt.hpp b/host/tests/math_test.cpp index d577ece1f..6c890c484 100644 --- a/host/utils/ihexcvt.hpp +++ b/host/tests/math_test.cpp @@ -14,9 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#ifndef _IHEXCVT_HPP_ -#define _IHEXCVT_HPP_ -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum); +#include <boost/test/unit_test.hpp> +#include <boost/cstdint.hpp> +#include <uhd/utils/math.hpp> + +// NOTE: This is not the only math test case, see e.g. special tests +// for fp comparison. + +BOOST_AUTO_TEST_CASE(test_log2){ + double y = uhd::math::log2(16.0); + BOOST_CHECK_EQUAL(y, 4.0); +} -#endif /* _IHEXCVT_HPP_ */ diff --git a/host/tests/ranges_c_test.c b/host/tests/ranges_c_test.c new file mode 100644 index 000000000..da9c86a48 --- /dev/null +++ b/host/tests/ranges_c_test.c @@ -0,0 +1,236 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#define UHD_TEST_EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + fprintf(stderr, "Error occurred at %s:%d\n", __FILE__, (__LINE__-1)); \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +#define UHD_TEST_CHECK_CLOSE(lhs, rhs) (fabs(lhs-rhs) < 0.001) + +#define BUFFER_SIZE 1024 + +static UHD_INLINE int test_range_values( + const uhd_range_t *range, + double start_input, double stop_input, double step_input +){ + if(!UHD_TEST_CHECK_CLOSE(range->start, start_input)){ + fprintf(stderr, "%s:%d: Starts did not match: %f vs. %f\n", + __FILE__, __LINE__, + range->start, start_input); + return 1; + } + if(!UHD_TEST_CHECK_CLOSE(range->stop, stop_input)){ + fprintf(stderr, "%s:%d: Stops did not match: %f vs. %f\n", + __FILE__, __LINE__, + range->stop, stop_input); + return 1; + } + if(!UHD_TEST_CHECK_CLOSE(range->step, step_input)){ + fprintf(stderr, "%s:%d: Steps did not match: %f vs. %f\n", + __FILE__, __LINE__, + range->step, step_input); + return 1; + } + + return 0; +} + +static UHD_INLINE int test_meta_range_values( + uhd_meta_range_handle meta_range, + double start_input, double stop_input, double step_input, + double start_test, double stop_test, double step_test +){ + // Add range + uhd_range_t range; + range.start = start_input; + range.stop = stop_input; + range.step = step_input; + if(uhd_meta_range_push_back(meta_range, &range)){ + fprintf(stderr, "%s:%d: Failed to push back range.\n", + __FILE__, __LINE__); + return 1; + } + + // Test bounds + uhd_meta_range_start(meta_range, &range.start); + if(!UHD_TEST_CHECK_CLOSE(range.start, start_test)){ + fprintf(stderr, "%s:%d: Starts did not match: %f vs. %f\n", + __FILE__, __LINE__, + range.start, start_test); + return 1; + } + uhd_meta_range_stop(meta_range, &range.stop); + if(!UHD_TEST_CHECK_CLOSE(range.stop, stop_test)){ + fprintf(stderr, "%s:%d: Stops did not match: %f vs. %f\n", + __FILE__, __LINE__, + range.stop, stop_test); + return 1; + } + uhd_meta_range_step(meta_range, &range.step); + if(!UHD_TEST_CHECK_CLOSE(range.step, step_test)){ + fprintf(stderr, "%s:%d: Steps did not match: %f vs. %f\n", + __FILE__, __LINE__, + range.step, step_test); + return 1; + } + + return 0; +} + +static UHD_INLINE int test_meta_range_clip( + uhd_meta_range_handle meta_range, + double clip_value, double test_value, + bool clip_step +){ + double clip_result; + + uhd_meta_range_clip(meta_range, clip_value, clip_step, &clip_result); + if(!UHD_TEST_CHECK_CLOSE(test_value, clip_result)){ + fprintf(stderr, "%s:%d: Values did not match: %f vs. %f\n", + __FILE__, __LINE__, + test_value, clip_result); + return 1; + } + + return 0; +} + +int main(){ + + // Variables + int return_code; + uhd_range_t range; + uhd_meta_range_handle meta_range1, meta_range2; + char str_buffer[BUFFER_SIZE]; + size_t size; + + return_code = EXIT_SUCCESS; + + // Create meta range 1 + UHD_TEST_EXECUTE_OR_GOTO(end_of_test, + uhd_meta_range_make(&meta_range1) + ) + + // Test bounds + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_values(meta_range1, -1.0, +1.0, 0.1, + -1.0, +1.0, 0.1) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_values(meta_range1, 40.0, 60.0, 1.0, + -1.0, 60.0, 0.1) + ) + uhd_meta_range_at(meta_range1, 0, &range); + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_range_values(&range, -1.0, +1.0, 0.1) + ) + + // Check meta range size + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + uhd_meta_range_size(meta_range1, &size) + ) + if(size != 2){ + fprintf(stderr, "%s:%d: Invalid size: %lu vs. 2", + __FILE__, __LINE__, + size); + goto free_meta_range1; + } + + // Test clipping (with steps) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, -30.0, -1.0, false) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, 70.0, 60.0, false) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, 20.0, 1.0, false) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, 50.0, 50.0, false) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, 50.9, 50.9, false) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + test_meta_range_clip(meta_range1, 50.9, 51.0, true) + ) + + // Create meta range 2 + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range1, + uhd_meta_range_make(&meta_range2) + ) + range.step = 0.0; + range.start = range.stop = 1.; + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + uhd_meta_range_push_back(meta_range2, &range) + ) + range.start = range.stop = 2.; + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + uhd_meta_range_push_back(meta_range2, &range) + ) + range.start = range.stop = 3.; + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + uhd_meta_range_push_back(meta_range2, &range) + ) + + // Test clipping (without steps) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + test_meta_range_clip(meta_range2, 2., 2., true) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + test_meta_range_clip(meta_range2, 0., 1., true) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + test_meta_range_clip(meta_range2, 1.2, 1., true) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + test_meta_range_clip(meta_range2, 3.1, 3., true) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_meta_range2, + test_meta_range_clip(meta_range2, 4., 3., true) + ) + + free_meta_range2: + if(return_code){ + uhd_meta_range_last_error(meta_range2, str_buffer, BUFFER_SIZE); + fprintf(stderr, "meta_range2 error: %s\n", str_buffer); + } + uhd_meta_range_free(&meta_range1); + + free_meta_range1: + if(return_code){ + uhd_meta_range_last_error(meta_range1, str_buffer, BUFFER_SIZE); + fprintf(stderr, "meta_range1 error: %s\n", str_buffer); + } + uhd_meta_range_free(&meta_range1); + + end_of_test: + if(!return_code){ + printf("\nNo errors detected.\n"); + } + return return_code; +} diff --git a/host/tests/sensors_c_test.c b/host/tests/sensors_c_test.c new file mode 100644 index 000000000..8babe905a --- /dev/null +++ b/host/tests/sensors_c_test.c @@ -0,0 +1,411 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#define UHD_TEST_EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + fprintf(stderr, "Error occurred at %s:%d\n", __FILE__, (__LINE__-1)); \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +#define UHD_TEST_CHECK_CLOSE(lhs, rhs) (fabs(lhs-rhs) < 0.001) + +#define BUFFER_SIZE 1024 + +int main(){ + + // Variables + int return_code; + uhd_sensor_value_handle boolean_sensor, integer_sensor, realnum_sensor, string_sensor; + uhd_sensor_value_data_type_t sensor_type; + bool bool_out; + int int_out; + double realnum_out; + char str_buffer[BUFFER_SIZE]; + + return_code = EXIT_SUCCESS; + + /* + * Test a sensor made from a boolean + */ + + // Create the sensor + UHD_TEST_EXECUTE_OR_GOTO(end_of_test, + uhd_sensor_value_make_from_bool( + &boolean_sensor, + "Bool sensor", false, + "True", "False" + ) + ) + + // Check the name + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_name( + boolean_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "Bool sensor")){ + fprintf(stderr, "%s:%d: Boolean sensor name invalid: \"%s\" vs. \"false\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_boolean_sensor; + } + + // Check the value + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_value( + boolean_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "false")){ + fprintf(stderr, "%s:%d: Boolean sensor value invalid: \"%s\" vs. \"false\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_boolean_sensor; + } + + // Check the unit + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_unit( + boolean_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "False")){ + fprintf(stderr, "%s:%d: Boolean sensor unit invalid: \"%s\" vs. \"False\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_boolean_sensor; + } + + // Check the type + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_data_type( + boolean_sensor, + &sensor_type + ) + ) + if(sensor_type != UHD_SENSOR_VALUE_BOOLEAN){ + fprintf(stderr, "%s:%d: Wrong sensor type detected: %d vs. %d\n", + __FILE__, __LINE__, + sensor_type, UHD_SENSOR_VALUE_BOOLEAN); + return_code = EXIT_FAILURE; + goto free_boolean_sensor; + } + + // Check the casted value + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_to_bool( + boolean_sensor, + &bool_out + ) + ) + if(bool_out){ + fprintf(stderr, "%s:%d: Boolean sensor value invalid: true vs. false\n", + __FILE__, __LINE__); + return_code = EXIT_FAILURE; + goto free_boolean_sensor; + } + + /* + * Test a sensor made from a integer + */ + + // Create the sensor + UHD_TEST_EXECUTE_OR_GOTO(free_boolean_sensor, + uhd_sensor_value_make_from_int( + &integer_sensor, + "Int sensor", 50, + "Int type", "%d" + ) + ) + + // Check the name + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_name( + integer_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "Int sensor")){ + fprintf(stderr, "%s:%d: Integer sensor name invalid: \"%s\" vs. \"Int sensor\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_integer_sensor; + } + + // Check the value + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_value( + integer_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "50")){ + fprintf(stderr, "%s:%d: Integer sensor value invalid: \"%s\" vs. \"50\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_integer_sensor; + } + + // Check the unit + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_unit( + integer_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "Int type")){ + fprintf(stderr, "%s:%d: Integer sensor unit invalid: \"%s\" vs. \"Int type\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_integer_sensor; + } + + // Check the type + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_data_type( + integer_sensor, + &sensor_type + ) + ) + if(sensor_type != UHD_SENSOR_VALUE_INTEGER){ + fprintf(stderr, "%s:%d: Wrong sensor type detected: %d vs. %d\n", + __FILE__, __LINE__, + sensor_type, UHD_SENSOR_VALUE_INTEGER); + return_code = EXIT_FAILURE; + goto free_integer_sensor; + } + + // Check the casted value + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_to_int( + integer_sensor, + &int_out + ) + ) + if(int_out != 50){ + fprintf(stderr, "%s:%d: Integer sensor value invalid: %d vs. 50\n", + __FILE__, __LINE__, + int_out); + return_code = EXIT_FAILURE; + goto free_integer_sensor; + } + + /* + * Test a sensor made from a real number + */ + + // Create the sensor + UHD_TEST_EXECUTE_OR_GOTO(free_integer_sensor, + uhd_sensor_value_make_from_realnum( + &realnum_sensor, + "Realnum sensor", 50.0, + "Realnum type", "%d" + ) + ) + + // Check the name + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_name( + realnum_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "Realnum sensor")){ + fprintf(stderr, "%s:%d: Realnum sensor name invalid: \"%s\" vs. \"Realnum sensor\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_realnum_sensor; + } + + // Check the value + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_value( + realnum_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "50")){ + fprintf(stderr, "%s:%d: Realnum sensor value invalid: \"%s\" vs. \"50\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_realnum_sensor; + } + + // Check the unit + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_unit( + realnum_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "Realnum type")){ + fprintf(stderr, "%s:%d: Realnum sensor unit invalid: \"%s\" vs. \"Realnum type\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_realnum_sensor; + } + + // Check the type + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_data_type( + realnum_sensor, + &sensor_type + ) + ) + if(sensor_type != UHD_SENSOR_VALUE_REALNUM){ + fprintf(stderr, "%s:%d: Wrong sensor type detected: %d vs. %d\n", + __FILE__, __LINE__, + sensor_type, UHD_SENSOR_VALUE_REALNUM); + return_code = EXIT_FAILURE; + goto free_realnum_sensor; + } + + // Check the casted value + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_to_realnum( + realnum_sensor, + &realnum_out + ) + ) + if(realnum_out != 50.0){ + fprintf(stderr, "%s:%d: Realnum sensor value invalid: %2.1f vs. 50.0\n", + __FILE__, __LINE__, + realnum_out); + return_code = EXIT_FAILURE; + goto free_realnum_sensor; + } + + /* + * Test a sensor made from a string + */ + + // Create the sensor + UHD_TEST_EXECUTE_OR_GOTO(free_realnum_sensor, + uhd_sensor_value_make_from_string( + &string_sensor, + "String sensor", + "String value", + "String unit" + ) + ) + + // Check the name + UHD_TEST_EXECUTE_OR_GOTO(free_string_sensor, + uhd_sensor_value_name( + string_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "String sensor")){ + fprintf(stderr, "%s:%d: String sensor name invalid: \"%s\" vs. \"String sensor\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_string_sensor; + } + + // Check the value + UHD_TEST_EXECUTE_OR_GOTO(free_string_sensor, + uhd_sensor_value_value( + string_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "String value")){ + fprintf(stderr, "%s:%d: String sensor value invalid: \"%s\" vs. \"String value\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_string_sensor; + } + + // Check the unit + UHD_TEST_EXECUTE_OR_GOTO(free_string_sensor, + uhd_sensor_value_unit( + string_sensor, + str_buffer, BUFFER_SIZE + ) + ) + if(strcmp(str_buffer, "String unit")){ + fprintf(stderr, "%s:%d: String sensor unit invalid: \"%s\" vs. \"String unit\"\n", + __FILE__, __LINE__, str_buffer); + return_code = EXIT_FAILURE; + goto free_string_sensor; + } + + // Check the type + UHD_TEST_EXECUTE_OR_GOTO(free_string_sensor, + uhd_sensor_value_data_type( + string_sensor, + &sensor_type + ) + ) + if(sensor_type != UHD_SENSOR_VALUE_STRING){ + fprintf(stderr, "%s:%d: Wrong sensor type detected: %d vs. %d\n", + __FILE__, __LINE__, + sensor_type, UHD_SENSOR_VALUE_STRING); + return_code = EXIT_FAILURE; + goto free_string_sensor; + } + + /* + * Cleanup + */ + + free_string_sensor: + if(return_code){ + uhd_sensor_value_last_error(string_sensor, str_buffer, BUFFER_SIZE); + fprintf(stderr, "string_sensor error: %s\n", str_buffer); + } + uhd_sensor_value_free(&string_sensor); + + free_realnum_sensor: + if(return_code){ + uhd_sensor_value_last_error(realnum_sensor, str_buffer, BUFFER_SIZE); + fprintf(stderr, "realnum_sensor error: %s\n", str_buffer); + } + uhd_sensor_value_free(&realnum_sensor); + + free_integer_sensor: + if(return_code){ + uhd_sensor_value_last_error(integer_sensor, str_buffer, BUFFER_SIZE); + fprintf(stderr, "integer_sensor error: %s\n", str_buffer); + } + uhd_sensor_value_free(&integer_sensor); + + free_boolean_sensor: + if(return_code){ + uhd_sensor_value_last_error(boolean_sensor, str_buffer, BUFFER_SIZE); + fprintf(stderr, "boolean_sensor error: %s\n", str_buffer); + } + uhd_sensor_value_free(&boolean_sensor); + + end_of_test: + if(!return_code){ + printf("\nNo errors detected.\n"); + } + return return_code; +} diff --git a/host/tests/sid_t_test.cpp b/host/tests/sid_t_test.cpp new file mode 100644 index 000000000..31eb4b458 --- /dev/null +++ b/host/tests/sid_t_test.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <sstream> +#include <boost/test/unit_test.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/exception.hpp> + +using uhd::sid_t; + +BOOST_AUTO_TEST_CASE(test_sid_t) { + boost::uint32_t sid_value = 0x01020310; + sid_t sid(sid_value); + + BOOST_CHECK_EQUAL(sid.is_set(), true); + BOOST_CHECK_EQUAL(sid.to_pp_string(), "1.2>3.16"); + BOOST_CHECK_EQUAL(sid.to_pp_string_hex(), "01:02>03:10"); + BOOST_CHECK_EQUAL(sid.get_src(), (boost::uint32_t)0x0102); + BOOST_CHECK_EQUAL(sid.get_dst(), (boost::uint32_t)0x0310); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x01); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x10); + BOOST_CHECK_EQUAL(sid == sid, true); + BOOST_CHECK_EQUAL(sid == sid_value, true); + + boost::uint32_t check_sid_val = (boost::uint32_t) sid; + BOOST_CHECK_EQUAL(check_sid_val, sid_value); + + std::stringstream ss_dec; + ss_dec << sid; + BOOST_CHECK_EQUAL(ss_dec.str(), "1.2>3.16"); + + std::stringstream ss_hex; + ss_hex << std::hex << sid; + BOOST_CHECK_EQUAL(ss_hex.str(), "01:02>03:10"); + + sid_t empty_sid; + BOOST_CHECK_EQUAL(empty_sid.is_set(), false); + BOOST_CHECK_EQUAL(empty_sid.to_pp_string(), "x.x>x.x"); + BOOST_CHECK_EQUAL(empty_sid.to_pp_string_hex(), "xx:xx>xx:xx"); + BOOST_CHECK_EQUAL(empty_sid == sid, false); + BOOST_CHECK_EQUAL(empty_sid == sid_value, false); + BOOST_CHECK_EQUAL((bool) empty_sid, false); + + empty_sid = sid_value; // No longer empty + BOOST_CHECK_EQUAL(empty_sid.is_set(), true); + BOOST_CHECK_EQUAL(empty_sid == sid, true); +} + +BOOST_AUTO_TEST_CASE(test_sid_t_set) { + boost::uint32_t sid_value = 0x0; + sid_t sid(sid_value); + + sid.set(0x01020304); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x01020304); + BOOST_CHECK_EQUAL(sid.get_src_addr(),(boost::uint32_t)0x01); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + BOOST_CHECK_EQUAL(sid.get_dst_xbarport(), (boost::uint32_t)0x0); + BOOST_CHECK_EQUAL(sid.get_dst_blockport(), (boost::uint32_t)0x4); + + sid.set_src_addr(0x0a); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a020304); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x02); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_src_endpoint(0x0b); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0304); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x03); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_dst_addr(0x0c); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0c04); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x04); + + sid.set_dst_endpoint(0x0d); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0c0d); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0x0d); + + sid.set_dst_xbarport(0xb); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0cbd); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0xbd); + + sid.set_dst_blockport(0xc); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0a0b0cbc); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)0x0a); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)0x0b); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)0x0c); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)0xbc); + + sid_t flipped_sid = sid.reversed(); + BOOST_CHECK_EQUAL(flipped_sid.get(), (boost::uint32_t)0x0cbc0a0b); + + // In-place + sid.reverse(); + BOOST_CHECK_EQUAL(sid.get(), (boost::uint32_t)0x0cbc0a0b); +} + +BOOST_AUTO_TEST_CASE(test_sid_t_from_str) { + sid_t sid("1.2>3.4"); + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)4); + + sid = "01:02>03:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + sid = "01:06/03:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)6); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)3); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + sid = "01:02:04:10"; + BOOST_CHECK_EQUAL(sid.get_src_addr(), (boost::uint32_t)1); + BOOST_CHECK_EQUAL(sid.get_src_endpoint(), (boost::uint32_t)2); + BOOST_CHECK_EQUAL(sid.get_dst_addr(), (boost::uint32_t)4); + BOOST_CHECK_EQUAL(sid.get_dst_endpoint(), (boost::uint32_t)16); + + BOOST_REQUIRE_THROW(sid_t fail_sid("foobar"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("01:02:03:4"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("01:02:03:004"), uhd::value_error); + BOOST_REQUIRE_THROW(sid_t fail_sid("1.2.3.0004"), uhd::value_error); +} diff --git a/host/tests/string_vector_c_test.c b/host/tests/string_vector_c_test.c new file mode 100644 index 000000000..fe055fd91 --- /dev/null +++ b/host/tests/string_vector_c_test.c @@ -0,0 +1,90 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define UHD_TEST_EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + fprintf(stderr, "Error occurred at %s:%d\n", __FILE__, (__LINE__-1)); \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +#define BUFFER_SIZE 1024 + +int main(){ + + // Variables + int return_code; + uhd_string_vector_handle string_vector; + size_t size; + char str_buffer[BUFFER_SIZE]; + + return_code = EXIT_SUCCESS; + + // Create string_vector + UHD_TEST_EXECUTE_OR_GOTO(end_of_test, + uhd_string_vector_make(&string_vector) + ) + + // Add values + UHD_TEST_EXECUTE_OR_GOTO(free_string_vector, + uhd_string_vector_push_back(&string_vector, "foo") + ) + UHD_TEST_EXECUTE_OR_GOTO(free_string_vector, + uhd_string_vector_push_back(&string_vector, "bar") + ) + + // Check size + UHD_TEST_EXECUTE_OR_GOTO(free_string_vector, + uhd_string_vector_size(string_vector, &size) + ) + if(size != 2){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Invalid size: %lu vs. 2", + __FILE__, __LINE__,size); + goto free_string_vector; + } + + // Make sure we get right value + UHD_TEST_EXECUTE_OR_GOTO(free_string_vector, + uhd_string_vector_at(string_vector, 1, str_buffer, BUFFER_SIZE) + ) + if(strcmp(str_buffer, "bar")){ + return_code = EXIT_FAILURE; + fprintf(stderr, "%s:%d: Mismatched daughterboard serial: \"%s\" vs. \"key3=value3,key4=value4\"\n", + __FILE__, __LINE__, + str_buffer); + } + + free_string_vector: + if(return_code){ + uhd_string_vector_last_error(string_vector, str_buffer, BUFFER_SIZE); + fprintf(stderr, "string_vector error: %s\n", str_buffer); + } + uhd_string_vector_free(&string_vector); + + end_of_test: + if(!return_code){ + printf("\nNo errors detected\n"); + } + return return_code; +} diff --git a/host/tests/subdev_spec_c_test.c b/host/tests/subdev_spec_c_test.c new file mode 100644 index 000000000..7663ba357 --- /dev/null +++ b/host/tests/subdev_spec_c_test.c @@ -0,0 +1,130 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd.h> + +#include <stdio.h> +#include <stdlib.h> + +#define UHD_TEST_EXECUTE_OR_GOTO(label, ...) \ + if(__VA_ARGS__){ \ + fprintf(stderr, "Error occurred at %s:%d\n", __FILE__, (__LINE__-1)); \ + return_code = EXIT_FAILURE; \ + goto label; \ + } + +#define BUFFER_SIZE 1024 + +int main(){ + + // Variables + int return_code; + uhd_subdev_spec_pair_t subdev_spec_pair1, subdev_spec_pair2; + uhd_subdev_spec_handle subdev_spec1, subdev_spec2; + size_t size1, size2, i; + bool pairs_equal; + char str_buffer[BUFFER_SIZE]; + + printf("Testing subdevice specification...\n"); + return_code = EXIT_SUCCESS; + + // Create subdev spec + UHD_TEST_EXECUTE_OR_GOTO(end_of_test, + uhd_subdev_spec_make(&subdev_spec1, "A:AB B:AB") + ) + + // Convert to and from args string + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec1, + uhd_subdev_spec_to_pp_string(subdev_spec1, str_buffer, BUFFER_SIZE) + ) + printf("Pretty Print:\n%s", str_buffer); + + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec1, + uhd_subdev_spec_to_string(subdev_spec1, str_buffer, BUFFER_SIZE) + ) + printf("Markup String: %s\n", str_buffer); + + // Make a second subdev spec from the first's markup string + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec2, + uhd_subdev_spec_make(&subdev_spec2, str_buffer) + ) + + // Make sure both subdev specs are equal + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec2, + uhd_subdev_spec_size(subdev_spec1, &size1) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec2, + uhd_subdev_spec_size(subdev_spec2, &size2) + ) + if(size1 != size2){ + printf("%s:%d: Sizes do not match. %lu vs. %lu\n", __FILE__, __LINE__, size1, size2); + return_code = EXIT_FAILURE; + goto free_subdev_spec2; + } + for(i = 0; i < size1; i++){ + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec_pair1, + uhd_subdev_spec_at(subdev_spec1, i, &subdev_spec_pair1) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec_pair2, + uhd_subdev_spec_at(subdev_spec2, i, &subdev_spec_pair2) + ) + UHD_TEST_EXECUTE_OR_GOTO(free_subdev_spec_pair2, + uhd_subdev_spec_pairs_equal(&subdev_spec_pair1, &subdev_spec_pair2, &pairs_equal) + ) + if(!pairs_equal){ + printf("%s:%d: Subdev spec pairs are not equal.\n" + " db_name: %s vs. %s\n" + " sd_name: %s vs. %s\n", + __FILE__, __LINE__, + subdev_spec_pair1.db_name, subdev_spec_pair2.db_name, + subdev_spec_pair1.sd_name, subdev_spec_pair2.sd_name + ); + return_code = EXIT_FAILURE; + goto free_subdev_spec_pair2; + } + uhd_subdev_spec_pair_free(&subdev_spec_pair1); + uhd_subdev_spec_pair_free(&subdev_spec_pair2); + } + + // Cleanup (and error report, if needed) + + free_subdev_spec_pair2: + uhd_subdev_spec_pair_free(&subdev_spec_pair2); + + free_subdev_spec_pair1: + uhd_subdev_spec_pair_free(&subdev_spec_pair1); + + free_subdev_spec2: + if(return_code){ + uhd_subdev_spec_last_error(subdev_spec2, str_buffer, BUFFER_SIZE); + fprintf(stderr, "subdev_spec2 error: %s\n", str_buffer); + } + uhd_subdev_spec_free(&subdev_spec2); + + free_subdev_spec1: + if(return_code){ + uhd_subdev_spec_last_error(subdev_spec1, str_buffer, BUFFER_SIZE); + fprintf(stderr, "subdev_spec1 error: %s\n", str_buffer); + } + uhd_subdev_spec_free(&subdev_spec1); + + end_of_test: + if(!return_code){ + printf("\nNo errors detected.\n"); + } + return return_code; +} diff --git a/host/tests/time_spec_test.cpp b/host/tests/time_spec_test.cpp index c9b9652f9..b98bea7d9 100644 --- a/host/tests/time_spec_test.cpp +++ b/host/tests/time_spec_test.cpp @@ -112,8 +112,8 @@ BOOST_AUTO_TEST_CASE(test_time_large_ticks_to_time_spec) BOOST_AUTO_TEST_CASE(test_time_error_irrational_rate) { - static const double rate = 1625e3/6; - const long long tick_in = 23423436291667; + static const double rate = 1625e3/6.0; + const long long tick_in = 23423436291667ll; const uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(tick_in, rate); const long long tick_out = ts.to_ticks(rate); const long long err = tick_in - tick_out; diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index e24b417c1..4f56dad0d 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2014 Ettus Research LLC +# Copyright 2010-2015 Ettus Research LLC # # 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 @@ -21,11 +21,11 @@ SET(util_runtime_sources uhd_find_devices.cpp uhd_usrp_probe.cpp + uhd_image_loader.cpp uhd_cal_rx_iq_balance.cpp uhd_cal_tx_dc_offset.cpp uhd_cal_tx_iq_balance.cpp usrp_n2xx_simple_net_burner.cpp - nirio_programmer.cpp ) SET(x3xx_burner_sources @@ -34,11 +34,11 @@ SET(x3xx_burner_sources ) find_package(UDev) -IF(ENABLE_E300) +IF(ENABLE_E300 AND NOT E300_FORCE_NETWORK) IF(UDEV_FOUND) LIST(APPEND util_runtime_sources usrp_e3x0_network_mode.cpp) ENDIF(UDEV_FOUND) -ENDIF(ENABLE_E300) +ENDIF(ENABLE_E300 AND NOT E300_FORCE_NETWORK) #for each source: build an executable and install FOREACH(util_source ${util_runtime_sources}) @@ -84,9 +84,11 @@ IF(ENABLE_OCTOCLOCK) SET(octoclock_burner_sources octoclock_firmware_burner.cpp - ihexcvt.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock/kk_ihex_read.c ) + ADD_DEFINITIONS(-DIHEX_USE_STDBOOL) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock) ADD_EXECUTABLE(octoclock_firmware_burner ${octoclock_burner_sources}) TARGET_LINK_LIBRARIES(octoclock_firmware_burner uhd ${Boost_LIBRARIES}) UHD_INSTALL(TARGETS octoclock_firmware_burner RUNTIME DESTINATION ${RUNTIME_DIR} COMPONENT utilities) diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index 572daef70..a3eaf862a 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -51,20 +51,27 @@ struct vid_pid_t { const static vid_pid_t known_vid_pids[] = { {FX3_VID, FX3_DEFAULT_PID}, {FX3_VID, FX3_REENUM_PID}, - {B200_VENDOR_ID, B200_PRODUCT_ID} - }; + {B200_VENDOR_ID, B200_PRODUCT_ID}, + {B200_VENDOR_ID, B205_PRODUCT_ID}, + {B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID}, + {B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID} +}; const static std::vector<vid_pid_t> known_vid_pid_vector(known_vid_pids, known_vid_pids + (sizeof(known_vid_pids) / sizeof(known_vid_pids[0]))); -const static boost::uint8_t eeprom_init_values[] = { - 0x43, - 0x59, - 0x14, - 0xB2, - (B200_PRODUCT_ID & 0xff), - (B200_PRODUCT_ID >> 8), - (B200_VENDOR_ID & 0xff), - (B200_VENDOR_ID >> 8) - }; -const static uhd::byte_vector_t eeprom_init_value_vector(eeprom_init_values, eeprom_init_values + (sizeof(eeprom_init_values) / sizeof(eeprom_init_values[0]))); + +static const size_t EEPROM_INIT_VALUE_VECTOR_SIZE = 8; +static uhd::byte_vector_t construct_eeprom_init_value_vector(boost::uint16_t vid, boost::uint16_t pid) +{ + uhd::byte_vector_t init_values(EEPROM_INIT_VALUE_VECTOR_SIZE); + init_values.at(0) = 0x43; + init_values.at(1) = 0x59; + init_values.at(2) = 0x14; + init_values.at(3) = 0xB2; + init_values.at(4) = static_cast<boost::uint8_t>(pid & 0xff); + init_values.at(5) = static_cast<boost::uint8_t>(pid >> 8); + init_values.at(6) = static_cast<boost::uint8_t>(vid & 0xff); + init_values.at(7) = static_cast<boost::uint8_t>(vid >> 8); + return init_values; +} //!used with lexical cast to parse a hex string template <class T> struct to_hex{ @@ -153,15 +160,22 @@ uhd::transport::usb_device_handle::sptr open_device(const boost::uint16_t vid, c try { // try caller's VID/PID first - handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); - if (user_supplied && handles.size() == 0) - std::cerr << (boost::format("Failed to open device with VID 0x%04x and PID 0x%04x - trying other known VID/PIDs") % vid % pid).str() << std::endl; - - // try known VID/PIDs next - for (size_t i = 0; handles.size() == 0 && i < known_vid_pid_vector.size(); i++) + std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> vid_pid_pair_list(1,uhd::transport::usb_device_handle::vid_pid_pair_t(vid,pid)); + handles = uhd::transport::usb_device_handle::get_device_list(vid_pid_pair_list); + if (handles.size() == 0) { - vp = known_vid_pid_vector[i]; - handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); + if (user_supplied) + { + std::cerr << (boost::format("Failed to open device with VID 0x%04x and PID 0x%04x - trying other known VID/PIDs") % vid % pid).str() << std::endl; + } + + // try known VID/PIDs next + for (size_t i = 0; handles.size() == 0 && i < known_vid_pid_vector.size(); i++) + { + vp = known_vid_pid_vector[i]; + handles = uhd::transport::usb_device_handle::get_device_list(vp.vid, vp.pid); + } + } if (handles.size() > 0) @@ -221,7 +235,7 @@ int read_eeprom(b200_iface::sptr& b200, uhd::byte_vector_t& data) int write_eeprom(b200_iface::sptr& b200, const uhd::byte_vector_t& data) { try { - b200->write_eeprom(0x0, 0x0, data); + b200->write_eeprom(0x0, 0x0, data); } catch (std::exception &e) { std::cerr << "Exception while writing EEPROM: " << e.what() << std::endl; return -1; @@ -281,7 +295,7 @@ int erase_eeprom(b200_iface::sptr& b200) boost::int32_t main(boost::int32_t argc, char *argv[]) { boost::uint16_t vid, pid; - std::string pid_str, vid_str, fw_file, fpga_file; + std::string pid_str, vid_str, fw_file, fpga_file, writevid_str, writepid_str; bool user_supplied_vid_pid = false; po::options_description visible("Allowed options"); @@ -295,7 +309,6 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { ("reset-device,D", "Reset the B2xx Device.") ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") ("reset-usb,U", "Reset the USB subsystem on your host computer.") - ("init-device,I", "Initialize a B2xx device.") ("load-fw,W", po::value<std::string>(&fw_file), "Load a firmware (hex) file into the FX3.") ("load-fpga,L", po::value<std::string>(&fpga_file), @@ -305,9 +318,14 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { // Hidden options provided for testing - use at your own risk! po::options_description hidden("Hidden options"); hidden.add_options() - ("uninit-device,U", "Uninitialize a B2xx device.") + ("init-device,I", "Initialize a B2xx device.") + ("uninit-device", "Uninitialize a B2xx device.") ("read-eeprom,R", "Read first 8 bytes of EEPROM") - ("erase-eeprom,E", "Erase first 8 bytes of EEPROM"); + ("erase-eeprom,E", "Erase first 8 bytes of EEPROM") + ("write-vid", po::value<std::string>(&writevid_str), + "Write VID field of EEPROM") + ("write-pid", po::value<std::string>(&writepid_str), + "Write PID field of EEPROM"); po::options_description desc; desc.add(visible); @@ -486,9 +504,24 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { * Cypress VID/PID for the initial FW load, but we can initialize from any state. */ if (vm.count("init-device")) { + uint16_t writevid = B200_VENDOR_ID; + uint16_t writepid = B200_PRODUCT_ID; + /* Now, initialize the device. */ - if (write_and_verify_eeprom(b200, eeprom_init_value_vector)) - return -1; + // Added for testing purposes - not exposed + if (vm.count("write-vid") && vm.count("write-pid")) + { + try { + writevid = atoh(writevid_str); + writepid = atoh(writepid_str); + } catch (std::exception &e) { + std::cerr << "Exception while parsing write VID and PID: " << e.what() << std:: endl; + return ~0; + } + } + + std::cout << "Writing VID and PID to EEPROM..." << std::endl << std::endl; + if (write_and_verify_eeprom(b200, construct_eeprom_init_value_vector(writevid, writepid))) return -1; std::cout << "EEPROM initialized, resetting device..." << std::endl << std::endl; diff --git a/tools/b200/b2x0_side_channel.py b/host/utils/b2xx_side_channel.py index cf88bd35f..070f5684c 100755 --- a/tools/b200/b2x0_side_channel.py +++ b/host/utils/b2xx_side_channel.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-2015 Ettus Research LLC # # 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 @@ -31,8 +31,11 @@ try: import usb.core import usb.util except Exception as e: - print "Failed to import usb - please have pyusb in your PYTHONPATH" - raise e + print("Failed to import module 'usb'.") + print("Please make sure you have PyUSB installed and in your PYTHONPATH.") + print("PyUSB PyPI website: https://pypi.python.org/pypi/pyusb") + print("To install, download from the website or use 'pip install pyusb'") + exit(1) VRT_OUT = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_OUT VRT_IN = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN @@ -161,6 +164,10 @@ USB_PHY_ERROR_REGISTERS = [ USB_ERROR_COUNTERS += USB_PHY_ERROR_REGISTERS +PIB_COUNTERS = [ + 'socket_inactive' +] + COUNTERS = [ 'magic', @@ -176,7 +183,13 @@ COUNTERS = [ 'heap_size', - 'resume_count' + 'resume_count', + 'state_transition_count', + 'invalid_gpif_state', + ('thread_0', PIB_COUNTERS), + ('thread_1', PIB_COUNTERS), + ('thread_2', PIB_COUNTERS), + ('thread_3', PIB_COUNTERS), ] USB_EVENTS = {} @@ -242,6 +255,101 @@ LTSSM_STATES[0x1c] = ['111XX', "(none)"] LTSSM_STATES[0x1f] = ['111XX', "(none)"] LTSSM_STATES[0x2c] = ['101100', "Cypress/Intel workaround"] +CF_NONE = 0 +CF_TX_SWING = 1 << 0 +CF_TX_DEEMPHASIS = 1 << 1 +CF_DISABLE_USB2 = 1 << 2 +CF_ENABLE_AS_SUPERSPEED = 1 << 3 +CF_PPORT_DRIVE_STRENGTH = 1 << 4 +CF_DMA_BUFFER_SIZE = 1 << 5 +CF_DMA_BUFFER_COUNT = 1 << 6 +CF_MANUAL_DMA = 1 << 7 +CF_SB_BAUD_DIV = 1 << 8 + +CF_RE_ENUM = 1 << 31 + +""" +typedef struct Config { + int tx_swing; // [90] [65] 45 + int tx_deemphasis; // 0x11 + int disable_usb2; // 0 + int enable_as_superspeed; // 1 + int pport_drive_strength; // CY_U3P_DS_THREE_QUARTER_STRENGTH + int dma_buffer_size; // [USB3] (max) + int dma_buffer_count; // [USB3] 1 + int manual_dma; // 0 + int sb_baud_div; // 434*2 +} CONFIG, *PCONFIG; + +typedef struct ConfigMod { + int flags; + CONFIG config; +} CONFIG_MOD, *PCONFIG_MOD; +""" + +class Config(): + def __init__(self, + tx_swing=None, tx_deemphasis=None, disable_usb2=None, enable_as_superspeed=None, + pport_drive_strength=None, + dma_buffer_size=None, dma_buffer_count=None, manual_dma=None, + sb_baud_div=None, + raw=None): + self.tx_swing = tx_swing + self.tx_deemphasis = tx_deemphasis + self.disable_usb2 = disable_usb2 + self.enable_as_superspeed = enable_as_superspeed + self.pport_drive_strength = pport_drive_strength + self.dma_buffer_size = dma_buffer_size + self.dma_buffer_count = dma_buffer_count + self.manual_dma = manual_dma + self.sb_baud_div = sb_baud_div + self._count = 9 + + if raw: + (self.tx_swing, + self.tx_deemphasis, + self.disable_usb2, + self.enable_as_superspeed, + self.pport_drive_strength, + self.dma_buffer_size, + self.dma_buffer_count, + self.manual_dma, + self.sb_baud_div) = struct.unpack("i"*self._count, raw) + def pack(self): + return struct.pack("i"*self._count, + self.tx_swing, + self.tx_deemphasis, + self.disable_usb2, + self.enable_as_superspeed, + self.pport_drive_strength, + self.dma_buffer_size, + self.dma_buffer_count, + self.manual_dma, + self.sb_baud_div) + def __str__(self): + return self.to_string() + def to_string(self, flags=-1): + s = "" + if flags & CF_TX_SWING: + s += "tx_swing = %s\n" % (self.tx_swing) + if flags & CF_TX_DEEMPHASIS: + s += "tx_deemphasis = %s\n" % (self.tx_deemphasis) + if flags & CF_DISABLE_USB2: + s += "disable_usb2 = %s\n" % (self.disable_usb2) + if flags & CF_ENABLE_AS_SUPERSPEED: + s += "enable_as_superspeed = %s\n" % (self.enable_as_superspeed) + if flags & CF_PPORT_DRIVE_STRENGTH: + s += "pport_drive_strength = %s\n" % (self.pport_drive_strength) + if flags & CF_DMA_BUFFER_SIZE: + s += "dma_buffer_size = %s\n" % (self.dma_buffer_size) + if flags & CF_DMA_BUFFER_COUNT: + s += "dma_buffer_count = %s\n" % (self.dma_buffer_count) + if flags & CF_MANUAL_DMA: + s += "manual_dma = %s\n" % (self.manual_dma) + if flags & CF_SB_BAUD_DIV: + s += "sb_baud_div = %s\n" % (self.sb_baud_div) + return s + def _parse_usb_event_log(data): l = [] for d in data: @@ -251,11 +359,11 @@ def _parse_usb_event_log(data): #l += [(USB_EVENTS[0x80][0] + "+%i" % (d & ~0x80), USB_EVENTS[0x80][1])] ltssm_key = (d & ~0x80) ltssm_val = "(unknown)" - if LTSSM_STATES.has_key(ltssm_key): + if ltssm_key in LTSSM_STATES: ltssm_val = LTSSM_STATES[ltssm_key][1] ltssm_val = "LTSSM: " + ltssm_val l += [(USB_EVENTS[0x80][0] + "+%i" % (d & ~0x80), ltssm_val)] - elif USB_EVENTS.has_key(d): + elif d in USB_EVENTS: l += [USB_EVENTS[d]] #else: # l += [("?", "?")] @@ -312,8 +420,8 @@ class counter_set(): try: vals = struct.unpack(self._fmt_str, data) self._update(vals) - except Exception, e: - print e + except Exception as e: + print(("While updating counter set '%s':" % self._name), e) def __str__(self): return self.to_string() @@ -345,7 +453,7 @@ class usb_device(): self.timeout = 2000 def open(self, idVendor, idProduct): - print "Finding %04x:%04x..." % (idVendor, idProduct) + print("Finding %04x:%04x..." % (idVendor, idProduct)) self.dev = usb.core.find(idVendor=idVendor, idProduct=idProduct) if self.dev is None: raise ValueError('Device not found: %04x:%04x' % (idVendor, idProduct)) @@ -361,7 +469,7 @@ class usb_device(): #self.dev.set_configuration() # This will throw as device is already claimed - print "Opened %04x:%04x" % (idVendor, idProduct) + print("Opened %04x:%04x" % (idVendor, idProduct)) #self.dev.ctrl_transfer(0x21, 0x09, 0, 0, [0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00] ) #self.dev.ctrl_transfer(bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength = None, timeout = None @@ -378,40 +486,41 @@ class usb_device(): l = [] try: l = self.dev.ctrl_transfer(VRT_IN, B200_VREQ_GET_USB_SPEED, 0, 0, 1) - except usb.core.USBError, e: + except usb.core.USBError as e: if e.errno == 32: - print e + print(e) + print("Is the firmware loaded?") sys.exit(0) if len(l) > 0: self.usb_speed = l[0] - print "Operating at USB", self.usb_speed + print("Operating at USB", self.usb_speed) break else: - print "Retrying..." + print("Retrying...") #if self.usb_speed == 3: # self.max_buffer_size = 512 - print "Max buffer size:", self.max_buffer_size - print + print("Max buffer size:", self.max_buffer_size) + print() def _handle_error(self, e, vrt): if e.errno == 19: # No such device raise e vrt_str = "0x%02x" % (vrt) - if VRQS.has_key(vrt): + if vrt in VRQS: vrt_str += " (%s)" % (VRQS[vrt]) - print "%s: %s" % (vrt_str, str(e)) + print("%s: %s" % (vrt_str, str(e))) def vrt_get(self, vrt): try: return self.dev.ctrl_transfer(VRT_IN, vrt, 0, 0, self.max_buffer_size, self.timeout) - except usb.core.USBError, e: + except usb.core.USBError as e: self._handle_error(e, vrt) return [] def vrt_set(self, vrt, data=""): try: return self.dev.ctrl_transfer(VRT_OUT, vrt, 0, 0, data, self.timeout) - except usb.core.USBError, e: + except usb.core.USBError as e: self._handle_error(e, vrt) return None @@ -427,7 +536,11 @@ class usb_device(): last = 0 while raw[last] != 0: try: - idx = raw.index(0, last) + try: + idx = raw.index(0, last) + except ValueError as e: + print("No null termination in log buffer (length: %d, last null: %d)" % (len(raw), last)) + break self.log_index += 1 line = "".join(map(chr, raw[last:idx])) #print "[%05i %05i] %s" % (self.log_index, self.log_read_count, line) @@ -438,8 +551,8 @@ class usb_device(): last = idx + 1 if last >= len(raw): break - except Exception, e: - print e + except Exception as e: + print("Exception while parsing log buffer:", e) break return lines @@ -449,8 +562,8 @@ class usb_device(): return for l in lines: #print l - print "[%05i %05i] %s" % (l[0], self.log_read_count, l[1]) - print + print("[%05i %05i] %s" % (l[0], self.log_read_count, l[1])) + print() def get_counters(self): data = self.vrt_get(B200_VREQ_GET_COUNTERS) @@ -461,8 +574,8 @@ class usb_device(): def print_counters(self): self.get_counters() - print "[%05i]" % (self.counters_read_count) - print self.counters + print("[%05i]" % (self.counters_read_count)) + print(self.counters) def get_usb_event_log(self): data = self.vrt_get(B200_VREQ_GET_USB_EVENT_LOG) @@ -479,8 +592,8 @@ class usb_device(): l = self.get_usb_event_log() if len(l) == 0: return - print "\n".join(map(lambda x: ("[%05i] " % (self.usb_event_log_read_count)) + x[0] + ":\t" + x[1], l)) - print + print("\n".join([("[%05i] " % (self.usb_event_log_read_count)) + x[0] + ":\t" + x[1] for x in l])) + print() def run_log(dev, options): items = [ @@ -488,8 +601,8 @@ def run_log(dev, options): (options.counters, dev.print_counters), (options.usb_events, dev.print_usb_event_log) ] - items = filter(lambda x: x[0] > 0, items) - smallest_interval = min(map(lambda x: x[0], items)) + items = [x for x in items if x[0] > 0] + smallest_interval = min([x[0] for x in items]) time_now = time.time() last = [time_now]*len(items) @@ -505,7 +618,7 @@ def run_log(dev, options): if time_now < (time_last + items[i][0]): continue if options.clear_screen and not cleared: - print chr(27) + "[2J" + print(chr(27) + "[2J") cleared = True #print items[i][1] items[i][1]() @@ -534,9 +647,9 @@ def recv_serial_data(ser): data = data[0:-2] if data == "": #print "[Received an empty line]" - print + print() elif data[0] == ' ': - print time_now_str, data[1:] + print(time_now_str, data[1:]) elif data[0] == 'U': data = data[1:] cur_type = 0 @@ -552,20 +665,20 @@ def recv_serial_data(ser): i = ord(c) - ord('A') cur_type = 2 else: - print time_now_str, "[Unknown type: '%s' (0x%02x) in '%s']" % (c, ord(c), data) + print(time_now_str, "[Unknown type: '%s' (0x%02x) in '%s']" % (c, ord(c), data)) elif cur_type == 1: i = ord(c) - ord('a') if (i < 0) or (i >= len(USB_PHY_ERROR_REGISTERS)): - print time_now_str, "[Unknown PHY error register index: '%s' (0x%02x) (%d) in '%s']" % (c, ord(c), i, data) + print(time_now_str, "[Unknown PHY error register index: '%s' (0x%02x) (%d) in '%s']" % (c, ord(c), i, data)) else: - print time_now_str, USB_PHY_ERROR_REGISTERS[i] + print(time_now_str, USB_PHY_ERROR_REGISTERS[i]) cur_type = 0 elif cur_type == 2: i2 = ord(c) - ord('A') if (c < 'A') or (c > 'P'): - print time_now_str, "[Unknown second USB event part: '%s' (0x%02x) (%d) in '%s']" % (c, ord(c), i2, data) + print(time_now_str, "[Unknown second USB event part: '%s' (0x%02x) (%d) in '%s']" % (c, ord(c), i2, data)) else: i = (i << 4) | i2 usb_events += [i] @@ -577,7 +690,7 @@ def recv_serial_data(ser): if len(usb_events) > 0: usb_event_log_read_count += 1 l = _parse_usb_event_log(usb_events) - print "\n".join(map(lambda x: time_now_str + ("[%05i] " % (usb_event_log_read_count)) + x[0] + ":\t" + x[1], l)) + print("\n".join([time_now_str + ("[%05i] " % (usb_event_log_read_count)) + x[0] + ":\t" + x[1] for x in l])) #print data = "" @@ -606,14 +719,14 @@ def main(): while True: try: ser = serial.Serial(port=options.tty, baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=None) # timeout: None (blocking), 0 (non-blocking) - print "Opened", options.tty + print("Opened", options.tty) try: recv_serial_data(ser) except KeyboardInterrupt: break - except Exception, e: - print e + except Exception as e: + print("Unable to open serial port:", e) break else: dev = usb_device() @@ -621,6 +734,12 @@ def main(): while True: try: dev.open(idVendor=hex_to_int(options.vid), idProduct=hex_to_int(options.pid)) + + raw_config = dev.vrt_get(B200_VREQ_GET_CONFIG) + current_config = Config(raw=raw_config) + print("Current config:") + print(current_config) + if options.flush_data_eps: dev.vrt_set(B200_VREQ_FLUSH_DATA_EPS) if options.sb_baud_div is not None: @@ -631,17 +750,66 @@ def main(): dev.vrt_set(B200_VREQ_CLEAR_COUNTERS) if options.fe_loopback is not None: dev.vrt_set(B200_VREQ_AD9361_LOOPBACK, struct.pack('B', int(options.fe_loopback))) + options.cmd = options.cmd.strip() if len(options.cmd) == 0: run_log(dev, options) - pass else: - pass - + cmds = options.cmd.split(',') + flags = 0 + for cmd in cmds: + cmd = cmd.strip() + if len(cmd) == 0: + continue + parts = cmd.split(' ') + action = parts[0].lower() + try: + if action == "txswing": + current_config.tx_swing = int(parts[1]) + flags |= CF_TX_SWING + elif action == "txdeemph": + current_config.tx_deemphasis = int(parts[1]) + flags |= CF_TX_DEEMPHASIS + elif action == "ss": + current_config.enable_as_superspeed = int(parts[1]) + flags |= CF_ENABLE_AS_SUPERSPEED + elif action == "disableusb2": + current_config.disable_usb2 = int(parts[1]) + flags |= CF_DISABLE_USB2 + elif action == "pportdrive": + current_config.pport_drive_strength = int(parts[1]) + flags |= CF_PPORT_DRIVE_STRENGTH + elif action == "dmasize": + current_config.dma_buffer_size = int(parts[1]) + flags |= CF_DMA_BUFFER_SIZE + elif action == "dmacount": + current_config.dma_buffer_count = int(parts[1]) + flags |= CF_DMA_BUFFER_COUNT + elif action == "manualdma": + current_config.manual_dma = int(parts[1]) + flags |= CF_MANUAL_DMA + elif action == "sbbauddiv": + current_config.sb_baud_div = int(parts[1]) + flags |= CF_SB_BAUD_DIV + elif action == "reenum": + flags |= CF_RE_ENUM + else: + print("'%s' not implemented" % (action)) + except Exception as e: + print("Exception while handling action '%s'" % (action), e) + if flags != 0: + print("New config to be set:") + print(current_config.to_string(flags)) + #print current_config + #print "Update flags: 0x%x" % (flags) + new_config = struct.pack("I", flags) + current_config.pack() + dev.vrt_set(B200_VREQ_SET_CONFIG, new_config) + else: + print("Not updating config") break - except usb.core.USBError, e: + except usb.core.USBError as e: if e.errno == 19: # No such device pass - print e + print(e) break return 0 diff --git a/host/utils/ihexcvt.cpp b/host/utils/ihexcvt.cpp deleted file mode 100644 index 0605ee61c..000000000 --- a/host/utils/ihexcvt.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* IHexCvt - Intel HEX File <=> Binary Converter (C++) - Copyright (C) 2014 Ali Nakisaee - -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 -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/ - -//Include needed stuff from C++ -#include <iostream> -#include <fstream> -#include <string> - -//... and also from C -#include <stdio.h> - -#include <boost/filesystem.hpp> - -#include <uhd/exception.hpp> -#include "ihexcvt.hpp" - -//Avoid repeating 'std::': -using namespace std; - -//The following function reads a hexadecimal number from a text file. -template <class T> -static bool ReadValueFromHex(ifstream& InputFile, T& outCh, unsigned char* ApplyChecksum) -{ - char V, L; - T X = 0; - outCh = 0; - - //Get the characters one by one. - //Remember: These values are big-endian. - //Remember: Every two hex characters (0-9/A-F) indicate ONE byte. - for (size_t i = 0; i < 2 * sizeof(T); i++) - { - InputFile.get( V ); - if (InputFile.fail()) - return false; - - X <<= 4; - if (V >= '0' && V <= '9') - L = (V - '0'); - else if (V >= 'a' && V <= 'f') - L = (V - 'a' + 10); - else if (V >= 'A' && V <= 'F') - L = (V - 'A' + 10); - else - return false; - X |= L; - - //Apply this character to the checksum - if (ApplyChecksum && i % 2 == 1) *ApplyChecksum += X & 0xFF; - } - - //Return... - outCh = X; - return true; -} - -//The following function writes a hexadecimal number from a text file. -template <class T> -static bool WriteHexValue(ofstream& OutFile, T Value, unsigned char* CalcChecksum) -{ - unsigned char V0 = 0; - char C; - - //Remember: These values are big-endian. - for (size_t i = 0; i < sizeof(T); i++) - { - //Get byte #i from the value. - V0 = (Value >> ((sizeof(T) - i - 1) * 8)) & 0xFF; - - //Extract the high nibble (4-bits) - if ((V0 & 0xF0) <= 0x90) - C = (V0 >> 4) + '0'; - else - C = (V0 >> 4) + ('A' - 10); - OutFile.put( C ); - - //Extract the low nibble (4-bits) - if ((V0 & 0xF) <= 0x9) - C = (V0 & 0xF) + '0'; - else - C = (V0 & 0xF) + ('A' - 10); - OutFile.put( C ); - - //Calculate the checksum - if (CalcChecksum) *CalcChecksum += V0; - } - return true; -} - -//Skip any incoming whitespaces -static void SkipWhitespace(ifstream& InputFile) -{ - for (;;) - { - char C; - InputFile.get(C); - if (InputFile.eof() || InputFile.fail()) break; - if (!(C == '\n' || C == '\r' || C == ' ' || C == '\t' || C == '\v')) - { - InputFile.putback(C); - break; - } - } -} - -//The function responsible for conversion from HEX files to BINary. -void Hex2Bin(const char* SrcName, const char* DstName, bool IgnoreChecksum) -{ - ifstream Src(SrcName); - if (Src.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - ofstream Dst(DstName, ios_base::binary); - if (Dst.bad()) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - - char Ch; - int LineIdx = 1; - - unsigned char ByteCount; - unsigned short AddressLow; - unsigned short Extra; - unsigned long ExtraL; - unsigned long AddressOffset = 0; - unsigned char RecordType; - unsigned char Data[255]; - unsigned char CurChecksum; - unsigned char FileChecksum; - bool EOFMarker = false; - bool EOFWarn = false; - - for ( ;; ) - { - Src.get(Ch); - if (Src.eof()) - break; - if (EOFMarker && !EOFWarn) - { - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); - } - if (Ch != ':') goto genericErr; - - CurChecksum = 0; - if (!ReadValueFromHex( Src, ByteCount, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, AddressLow, &CurChecksum )) goto genericErr; - if (!ReadValueFromHex( Src, RecordType, &CurChecksum )) goto genericErr; - - switch (RecordType) - { - case 0x00: //Data record - for (int i = 0; i < ByteCount; i++) - if (!ReadValueFromHex( Src, Data[i], &CurChecksum )) goto genericErr; - break; - case 0x01: //End Marker - if ( ByteCount != 0 ) - { - goto onErrExit; - } - EOFMarker = true; - break; - case 0x02: //Extended Segment Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 4; - break; - case 0x03: //Start Segment Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - case 0x04: //Extended Linear Address - if ( ByteCount != 2 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, Extra, &CurChecksum )) goto genericErr; - AddressOffset = (unsigned long)Extra << 16; - break; - case 0x05: //Start Linear Address - if ( ByteCount != 4 || AddressLow != 0 ) - { - goto onErrExit; - } - if (!ReadValueFromHex( Src, ExtraL, &CurChecksum )) goto genericErr; - break; - } - - //Verify checksum - CurChecksum = (~(CurChecksum & 0xFF) + 1) & 0xFF; - if (!ReadValueFromHex( Src, FileChecksum, NULL )) goto genericErr; - if (CurChecksum != FileChecksum) - { - if (!IgnoreChecksum) goto onErrExit; - } - - //Put Data - if (RecordType == 0x00) - { - Dst.seekp( AddressLow + AddressOffset ); - for (int i = 0; i < ByteCount; i++) - { - Dst.put( Data[i] ); - } - } - - //Skip any white space - SkipWhitespace( Src ); - - LineIdx++; - } - - Dst << flush; - Dst.close(); - - return; - -genericErr: - throw uhd::runtime_error("Invalid Intel .hex file detected."); - -onErrExit: - Dst.close(); - Src.close(); - boost::filesystem::remove(DstName); - throw uhd::runtime_error("Could not convert Intel .hex file to binary."); -} diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp index d624095e6..272394860 100644 --- a/host/utils/octoclock_firmware_burner.cpp +++ b/host/utils/octoclock_firmware_burner.cpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // 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 @@ -42,7 +42,7 @@ #include <uhd/utils/safe_main.hpp> #include "../lib/usrp_clock/octoclock/common.h" -#include "ihexcvt.hpp" +#include "kk_ihex_read.h" #define MAX_FIRMWARE_SIZE 1024*120 #define BLOCK_SIZE 256 @@ -74,8 +74,23 @@ boost::uint8_t firmware_image[MAX_FIRMWARE_SIZE]; size_t firmware_size = 0; boost::uint8_t octoclock_data[udp_simple::mtu]; octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t *>(octoclock_data); -std::string firmware_path; +std::string firmware_path, actual_firmware_path; size_t num_blocks = 0; +bool hex = true; + +static uint16_t calculate_crc(boost::uint8_t* buffer, boost::uint16_t len){ + boost::uint16_t crc = 0xFFFF; + + for(size_t i = 0; i < len; i++){ + crc ^= buffer[i]; + for(boost::uint8_t j = 0; j < 8; ++j){ + if(crc & 1) crc = (crc >> 1) ^ 0xA001; + else crc = (crc >> 1); + } + } + + return crc; +} /* * Functions @@ -89,6 +104,28 @@ void list_octoclocks(){ } } +void print_image_loader_warning(const std::string &fw_path, const po::variables_map &vm){ + // Newline + indent + #ifdef UHD_PLATFORM_WIN32 + const std::string nl = " ^\n "; + #else + const std::string nl = " \\\n "; + #endif + + std::string uhd_image_loader = str(boost::format("uhd_image_loader --args=\"type=octoclock,addr=%s\"" + "%s --fw-path=%s") + % vm["addr"].as<std::string>() % nl % fw_path); + + std::cout << "************************************************************************************************" << std::endl + << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl + << " this command:" << std::endl + << std::endl + << uhd_image_loader << std::endl + << std::endl + << "************************************************************************************************" << std::endl + << std::endl; +} + /* * Manually find bootloader. This sends multiple packets in order to increase chances of getting * bootloader before it switches to the application. @@ -121,26 +158,25 @@ device_addrs_t bootloader_find(const std::string &ip_addr){ } void read_firmware(){ - std::ifstream firmware_file(firmware_path.c_str(), std::ios::binary); - firmware_file.seekg(0, std::ios::end); - firmware_size = size_t(firmware_file.tellg()); + std::ifstream firmware_file(actual_firmware_path.c_str(), std::ios::binary); + firmware_size = size_t(fs::file_size(actual_firmware_path)); if(firmware_size > MAX_FIRMWARE_SIZE){ firmware_file.close(); throw uhd::runtime_error(str(boost::format("Firmware file too large: %d > %d") % firmware_size % (MAX_FIRMWARE_SIZE))); } - firmware_file.seekg(0, std::ios::beg); firmware_file.read((char*)firmware_image, firmware_size); firmware_file.close(); - num_blocks = (firmware_size % BLOCK_SIZE) ? (firmware_size / BLOCK_SIZE) - : ((firmware_size / BLOCK_SIZE) + 1); + num_blocks = (firmware_size % BLOCK_SIZE) ? ((firmware_size / BLOCK_SIZE) + 1) + : (firmware_size / BLOCK_SIZE); } void burn_firmware(udp_simple::sptr udp_transport){ octoclock_packet_t pkt_out; pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand()); - pkt_out.len = uhd::htonx<boost::uint16_t>((boost::uint16_t)firmware_size); + pkt_out.len = (boost::uint16_t)firmware_size; + pkt_out.crc = calculate_crc(firmware_image, firmware_size); size_t len = 0, current_pos = 0; //Tell OctoClock not to jump to application, wait for us instead @@ -149,6 +185,7 @@ void burn_firmware(udp_simple::sptr udp_transport){ if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)) std::cout << "ready." << std::endl; else{ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Could not get OctoClock in valid state for firmware download."); } @@ -165,7 +202,7 @@ void burn_firmware(udp_simple::sptr udp_transport){ << "% (" << (i+1) << "/" << num_blocks << " blocks)" << std::flush; memset(pkt_out.data, 0, BLOCK_SIZE); - memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], std::min(int(firmware_size-current_pos), BLOCK_SIZE)); + memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], BLOCK_SIZE); bool success = false; while(num_tries <= 5){ @@ -181,6 +218,7 @@ void burn_firmware(udp_simple::sptr udp_transport){ } if(not success){ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to burn firmware to OctoClock!"); } @@ -196,7 +234,6 @@ void verify_firmware(udp_simple::sptr udp_transport){ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand()); size_t len = 0, current_pos = 0; - for(size_t i = 0; i < num_blocks; i++){ pkt_out.sequence++; pkt_out.addr = i*BLOCK_SIZE; @@ -208,11 +245,13 @@ void verify_firmware(udp_simple::sptr udp_transport){ if(memcmp((void*)(pkt_in->data), &firmware_image[i*BLOCK_SIZE], std::min(int(firmware_size-current_pos), BLOCK_SIZE))){ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to verify OctoClock firmware!"); } } else{ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to verify OctoClock firmware!"); } } @@ -230,6 +269,7 @@ bool reset_octoclock(const std::string &ip_addr){ UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, RESET_CMD, pkt_out, len, octoclock_data); if(not UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to place device in state to receive firmware."); } @@ -246,11 +286,32 @@ void finalize(udp_simple::sptr udp_transport){ UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, FINALIZE_BURNING_CMD, pkt_out, len, octoclock_data); if(not UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); std::cout << "no ACK. Bootloader may not have loaded application." << std::endl; } } -int UHD_SAFE_MAIN(int argc, char *argv[]){ +void octoclock_convert_ihex(const std::string &hex_path, const std::string &bin_path){ + struct ihex_state ihex; + ihex_count_t count; + char buf[256]; + FILE* infile = fopen(hex_path.c_str(), "r"); + FILE* outfile = fopen(bin_path.c_str(), "w"); + uint64_t line_number = 1; + + ihex_begin_read(&ihex); + while(fgets(buf, 256, infile)){ + count = ihex_count_t(strlen(buf)); + ihex_read_bytes(&ihex, buf, count, outfile); + line_number += (count && buf[count - 1] == '\n'); + } + ihex_end_read(&ihex, outfile); // Closes outfile + + (void)fclose(infile); +} + +int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){ + std::string ip_addr; po::options_description desc("Allowed options"); desc.add_options() @@ -300,7 +361,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ throw uhd::runtime_error(str(boost::format("This filepath does not exist: %s") % firmware_path)); } } - else firmware_path = find_image_path("octoclock_r4_fw.bin"); + else firmware_path = find_image_path("octoclock_r4_fw.hex"); //If Intel hex file detected, convert to binary std::string ext = fs::extension(firmware_path); @@ -310,11 +371,13 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //Write firmware .bin file to temporary directory fs::path temp_bin = fs::path(fs::path(get_tmp_path()) / str(boost::format("octoclock_fw_%d.bin") % time_spec_t::get_system_time().get_full_secs())); - Hex2Bin(firmware_path.c_str(), temp_bin.string().c_str(), false); + octoclock_convert_ihex(firmware_path, temp_bin.string()); - firmware_path = temp_bin.string(); + actual_firmware_path = temp_bin.string(); } else if(ext == ".bin"){ + hex = false; + actual_firmware_path = firmware_path; std::cout << "Found firmware at path: " << firmware_path << std::endl; } else throw uhd::runtime_error("The firmware file has in improper extension (must be .hex or .bin)."); @@ -327,6 +390,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if(reset_octoclock(ip_addr)) std::cout << "successful." << std::endl; else{ std::cout << "failed." << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to reset OctoClock device into its bootloader."); } } @@ -334,11 +398,14 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } else{ std::cout << "failed." << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Could not find OctoClock with given IP address!"); } read_firmware(); + print_image_loader_warning(firmware_path, vm); + std::signal(SIGINT, &sig_int_handler); burn_firmware(udp_transport); @@ -354,7 +421,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ if(octoclocks.size() == 1){ if(octoclocks[0]["type"] == "octoclock-bootloader"){ std::cout << std::endl; - throw uhd::runtime_error("OctoClock failed to leave bootloader state."); + if(hex) fs::remove(actual_firmware_path); + throw uhd::runtime_error("Firmware did not load properly."); } else{ std::cout << "found." << std::endl << std::endl @@ -363,8 +431,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } else{ std::cout << std::endl; + if(hex) fs::remove(actual_firmware_path); throw uhd::runtime_error("Failed to reinitialize OctoClock."); } + if(hex) fs::remove(actual_firmware_path); return EXIT_SUCCESS; } diff --git a/host/utils/uhd_image_loader.cpp b/host/utils/uhd_image_loader.cpp new file mode 100644 index 000000000..5ea789ee2 --- /dev/null +++ b/host/utils/uhd_image_loader.cpp @@ -0,0 +1,127 @@ +// +// Copyright 2015 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <csignal> +#include <cstdlib> +#include <iostream> + +#include <boost/assign.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> + +#include <uhd/config.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/safe_main.hpp> +#include <boost/program_options.hpp> + +namespace fs = boost::filesystem; +namespace po = boost::program_options; + +static std::string device_type = ""; +static int num_ctrl_c = 0; + +/* + * If the user presses Ctrl+C, warn them that they may corrupt their device. + * If they press it again, provide instructions on restoring the device + * (if applicable) and exit. + */ +void sigint_handler(int){ + num_ctrl_c++; + if(num_ctrl_c == 1){ + std::cout << std::endl + << "Are you sure you want to abort? If you do, your device will likely" << std::endl + << "be in an unstable or unusable state." << std::endl + << "Press Ctrl+C again to abort." << std::endl << std::endl; + } + else{ + std::cout << std::endl << uhd::image_loader::get_recovery_instructions(device_type) << std::endl; + exit(EXIT_FAILURE); + } +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + + std::string fw_path = ""; + std::string fpga_path = ""; + + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>()->default_value(""), "Device args, optional loader args") + ("fw-path", po::value<std::string>(&fw_path)->default_value(""), "Firmware path (uses default if none specified)") + ("fpga-path", po::value<std::string>(&fpga_path)->default_value(""), "FPGA path (uses default if none specified)") + ("no-fw", "Don't burn firmware") + ("no-fpga", "Don't burn FPGA") + ; + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + // Help message + if (vm.count("help")){ + std::cout << "UHD Image Loader" << std::endl + << std::endl + << "Load firmware and/or FPGA images onto an Ettus Research device." << std::endl + << std::endl + << desc << std::endl; + return EXIT_FAILURE; + } + + // Convert user options + uhd::image_loader::image_loader_args_t image_loader_args; + image_loader_args.args = vm["args"].as<std::string>(); + image_loader_args.load_firmware = (vm.count("no-fw") == 0); + image_loader_args.load_fpga = (vm.count("no-fpga") == 0); + image_loader_args.firmware_path = vm["fw-path"].as<std::string>(); + image_loader_args.fpga_path = vm["fpga-path"].as<std::string>(); + + // Force user to specify a device + if(not image_loader_args.args.has_key("type")){ + throw uhd::runtime_error("You must specify a device type."); + } + + // Clean up paths, if given + if(image_loader_args.firmware_path != ""){ + #ifndef UHD_PLATFORM_WIN32 + if(image_loader_args.firmware_path.find("~") == 0){ + image_loader_args.firmware_path.replace(0,1,getenv("HOME")); + } + #endif /* UHD_PLATFORM_WIN32 */ + image_loader_args.firmware_path = fs::absolute(image_loader_args.firmware_path).string(); + } + if(image_loader_args.fpga_path != ""){ + #ifndef UHD_PLATFORM_WIN32 + if(image_loader_args.fpga_path.find("~") == 0){ + image_loader_args.fpga_path.replace(0,1,getenv("HOME")); + } + #endif /* UHD_PLATFORM_WIN32 */ + image_loader_args.fpga_path = fs::absolute(image_loader_args.fpga_path).string(); + } + + // Detect which type of device we're working with + device_type = image_loader_args.args.get("type",""); + + std::signal(SIGINT, &sigint_handler); + if(not uhd::image_loader::load(image_loader_args)){ + std::cerr << "No applicable UHD devices found" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/host/utils/uhd_images_downloader.py.in b/host/utils/uhd_images_downloader.py.in index 3903edc8c..e0375fb43 100644 --- a/host/utils/uhd_images_downloader.py.in +++ b/host/utils/uhd_images_downloader.py.in @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2012-2014 Ettus Research LLC +# Copyright 2012-2015 Ettus Research LLC # # 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 @@ -23,7 +23,7 @@ import math import traceback import shutil import hashlib -import urllib2 +import requests import zipfile from optparse import OptionParser @@ -47,8 +47,8 @@ def md5Checksum(filePath): break m.update(data) return m.hexdigest() - except Exception, e: - print "Failed to calculated MD5 sum of: %s (%s)" % (filePath, e) + except Exception as e: + print("Failed to calculated MD5 sum of: %s (%s)" % (filePath, e)) raise e _checksum_fns = { @@ -60,16 +60,16 @@ class temporary_directory(): try: self.name = tempfile.mkdtemp() return self.name - except Exception, e: - print "Failed to create a temporary directory (%s)" % (e) + except Exception as e: + print("Failed to create a temporary directory (%s)" % (e)) raise e # Can return 'True' to suppress incoming exception def __exit__(self, type, value, traceback): try: shutil.rmtree(self.name) - except Exception, e: - print "Could not delete temporary directory: %s (%s)" % (self.name, e) + except Exception as e: + print("Could not delete temporary directory: %s (%s)" % (self.name, e)) class uhd_images_downloader(): def __init__(self): @@ -77,25 +77,20 @@ class uhd_images_downloader(): def download(self, images_url, filename, buffer_size=_DEFAULT_BUFFER_SIZE, print_progress=False): """ Run the download, show progress """ - opener = urllib2.build_opener() - opener.add_headers = [('User-Agent', 'UHD Images Downloader')] - u = opener.open(images_url) - meta = u.info() - filesize = float(meta.getheaders("Content-Length")[0]) + r = requests.get(images_url, stream=True, headers={'User-Agent': 'UHD Images Downloader'}) + filesize = float(r.headers['content-length']) filesize_dl = 0 with open(filename, "wb") as f: - while True: - buff = u.read(buffer_size) - if not buff: - break - f.write(buff) - filesize_dl += len(buff) + for buff in r.iter_content(chunk_size=buffer_size): + if buff: + f.write(buff) + filesize_dl += len(buff) if print_progress: status = r"%05d kB / %05d kB (%03d%%)" % (int(math.ceil(filesize_dl/1000.)), int(math.ceil(filesize/1000.)), int(math.ceil(filesize_dl*100.)/filesize)) status += chr(8)*(len(status)+1) - print status, + sys.stdout.write(status) if print_progress: - print + print('') return (filesize, filesize_dl) def check_directories(self, dirs, print_progress=False): @@ -105,23 +100,23 @@ class uhd_images_downloader(): def _check_part(head, tail=None): if print_progress: - print "Checking: %s" % (head) + print("Checking: %s" % (head)) if tail is not None and tail == "": return True if not os.path.exists(head): if print_progress: - print "Does not exist: %s" % (head) + print("Does not exist: %s" % (head)) return _check_part(*os.path.split(head)) if not os.path.isdir(head): if print_progress: - print "Is not a directory: %s" % (head) + print("Is not a directory: %s" % (head)) return (False, head) if not os.access(head, os.W_OK): if print_progress: - print "Write permission denied on: %s" % (head) + print("Write permission denied on: %s" % (head)) return (False, head) if print_progress: - print "Write permission granted on: %s" % (head) + print("Write permission granted on: %s" % (head)) return (True, head) return _check_part(dirs) @@ -137,34 +132,34 @@ class uhd_images_downloader(): def extract_images_archive(self, archive_path, destination=None, print_progress=False): if not os.path.exists(archive_path): if print_progress: - print "Path does not exist: %s" % (archive_path) + print("Path does not exist: %s" % (archive_path)) raise Exception("path does not exist: %s" % (archive_path)) if print_progress: - print "Archive path: %s" % (archive_path) + print("Archive path: %s" % (archive_path)) (head, tail) = os.path.split(archive_path) if not os.access(head, os.W_OK): if print_progress: - print "Write access denied on: %s" % (head) + print("Write access denied on: %s" % (head)) raise Exception("write access denied on: %s" % (head)) (root, ext) = os.path.splitext(tail) temp_dir = os.path.join(head, root) if print_progress: - print "Temporary extraction location: %s" % (temp_dir) + print("Temporary extraction location: %s" % (temp_dir)) if os.path.exists(temp_dir): if print_progress: - print "Deleting existing location: %s" % (temp_dir) + print("Deleting existing location: %s" % (temp_dir)) shutil.rmtree(temp_dir) if print_progress: - print "Creating directory: %s" % (temp_dir) + print("Creating directory: %s" % (temp_dir)) os.mkdir(temp_dir) if print_progress: - print "Extracting archive %s to %s" % (archive_path, temp_dir) + print("Extracting archive %s to %s" % (archive_path, temp_dir)) images_zip = zipfile.ZipFile(archive_path) images_zip.extractall(temp_dir) @@ -175,27 +170,27 @@ class uhd_images_downloader(): def install_images(self, source, dest, keep=False, print_progress=False): if not os.path.exists(source): if print_progress: - print "Source path does not exist: %s" % (source) + print("Source path does not exist: %s" % (source)) return if keep: if print_progress: - print "Not wiping directory tree (existing files will be overwritten): %s" % (dest) + print("Not wiping directory tree (existing files will be overwritten): %s" % (dest)) elif os.path.exists(dest): if print_progress: - print "Deleting directory tree: %s" % (dest) + print("Deleting directory tree: %s" % (dest)) shutil.rmtree(dest) (head, tail) = os.path.split(source) if print_progress: - print "Source install path: %s" % (source) + print("Source install path: %s" % (source)) uhd_source = os.path.join(source, tail, *_BASE_DIR_STRUCTURE_PARTS) if print_progress: - print "Copying files from: %s" % (uhd_source) - print "Copying files to: %s" % (dest) + print("Copying files from: %s" % (uhd_source)) + print("Copying files to: %s" % (dest)) if keep: # mgrant @ http://stackoverflow.com/questions/12683834/how-to-copy-directory-recursively-in-python-and-overwrite-all @@ -222,12 +217,12 @@ def main(): ### Set defaults from env variables if os.environ.get("UHD_IMAGES_DIR") != None and os.environ.get("UHD_IMAGES_DIR") != "": default_images_dir = os.environ.get("UHD_IMAGES_DIR") - print "UHD_IMAGES_DIR environment variable is set.\nDefault install location: {0}".format(default_images_dir) + print("UHD_IMAGES_DIR environment variable is set.\nDefault install location: {0}".format(default_images_dir)) else: default_images_dir = _DEFAULT_INSTALL_PATH if os.environ.get("UHD_IMAGES_BASE_URL") != None and os.environ.get("UHD_IMAGES_BASE_URL") != "": default_base_url = os.environ.get("UHD_IMAGES_BASE_URL") - print "UHD_IMAGES_BASE_URL environment variable is set.\nDefault base URL: {0}".format(default_base_url) + print("UHD_IMAGES_BASE_URL environment variable is set.\nDefault base URL: {0}".format(default_base_url)) else: default_base_url = _DEFAULT_BASE_URL @@ -244,22 +239,22 @@ def main(): parser.add_option("-c", "--checksum", type="string", default=_AUTOGEN_IMAGES_CHECKSUM, help="Validate images archive against this checksum (blank to skip) [default=%default]") parser.add_option("-t", "--checksum-type", type="string", default=_IMAGES_CHECKSUM_TYPE, - help=("Select checksum hash function (options: %s) [default=%%default]" % (",".join(_checksum_fns.keys())))) + help=("Select checksum hash function (options: %s) [default=%%default]" % (",".join(list(_checksum_fns.keys()))))) parser.add_option("-k", "--keep", action="store_true", default=False, help="Do not clear images directory before extracting new files [default=%default]") parser.add_option("-v", "--verbose", action="store_true", default=False, help="Enable verbose output [default=%default]") (options, args) = parser.parse_args() if options.buffer_size <= 0: - print "Invalid buffer size: %s" % (options.buffer_size) + print("Invalid buffer size: %s" % (options.buffer_size)) return 1 ### Select checksum algorithm (MD5) checksum_fn = None if options.checksum != "": options.checksum_type = options.checksum_type.lower() - if not _checksum_fns.has_key(options.checksum_type): - print "Not a supported checksum function: %s" % (options.checksum_type) + if options.checksum_type not in _checksum_fns: + print("Not a supported checksum function: %s" % (options.checksum_type)) return 1 checksum_fn = _checksum_fns[options.checksum_type] @@ -275,30 +270,30 @@ def main(): base_url_is_local = True if options.verbose: - print "Requested install location: %s" % (options.install_location) - print "Images base URL: %s" % (options.base_url) - print "Images filename: %s" % (options.filename) - print "Images checksum: %s (%s)" % (options.checksum, _IMAGES_CHECKSUM_TYPE) - print "Final install location: %s" % (images_dir) - print "Copying locally: {0}".format("Yes" if base_url_is_local else "No") + print("Requested install location: %s" % (options.install_location)) + print("Images base URL: %s" % (options.base_url)) + print("Images filename: %s" % (options.filename)) + print("Images checksum: %s (%s)" % (options.checksum, _IMAGES_CHECKSUM_TYPE)) + print("Final install location: %s" % (images_dir)) + print("Copying locally: {0}".format("Yes" if base_url_is_local else "No")) else: - print "Images destination: %s" % (images_dir) + print("Images destination: %s" % (images_dir)) ### Download or copy downloader = uhd_images_downloader() try: (access, last_path) = downloader.check_directories(images_dir, print_progress=options.verbose) if not access: - print "You do not have sufficient permissions to write to: %s" % (last_path) - print "Are you root?" + print("You do not have sufficient permissions to write to: %s" % (last_path)) + print("Are you root?") return 1 with temporary_directory() as temp_dir: if options.verbose: - print "Using temporary directory: %s" % (temp_dir) + print("Using temporary directory: %s" % (temp_dir)) temp_images_dest = os.path.join(temp_dir, options.filename) if not base_url_is_local: - print "Downloading images from: {0}".format(images_url) - print "Downloading images to: {0}".format(temp_images_dest) + print("Downloading images from: {0}".format(images_url)) + print("Downloading images to: {0}".format(temp_images_dest)) (reported_size, downloaded_size) = downloader.download( images_url=images_url, filename=temp_images_dest, @@ -306,12 +301,12 @@ def main(): print_progress=True ) if options.verbose: - print "Downloaded %d of %d bytes" % (downloaded_size, reported_size) + print("Downloaded %d of %d bytes" % (downloaded_size, reported_size)) else: local_images_pkg = os.path.join(options.base_url, options.filename) - print "Copying images from: {0}".format(local_images_pkg) + print("Copying images from: {0}".format(local_images_pkg)) if not os.path.isfile(local_images_pkg): - print "[ERROR] No such file." + print("[ERROR] No such file.") return 1 shutil.copyfile(local_images_pkg, temp_images_dest) (checksum_match, calculated_checksum) = downloader.validate_checksum( @@ -321,48 +316,46 @@ def main(): print_progress=options.verbose ) if options.verbose: - print "Calculated checksum: %s" % (calculated_checksum) + print("Calculated checksum: %s" % (calculated_checksum)) if checksum_match: if options.verbose: if options.checksum == "": - print "Ignoring checksum" + print("Ignoring checksum") else: - print "Checksum OK" + print("Checksum OK") try: extract_path = downloader.extract_images_archive(temp_images_dest, print_progress=options.verbose) if options.verbose: - print "Image archive extracted to: %s" % (extract_path) + print("Image archive extracted to: %s" % (extract_path)) downloader.install_images(extract_path, images_dir, options.keep, print_progress=options.verbose) if options.verbose: - print "Cleaning up temp location: %s" % (extract_path) + print("Cleaning up temp location: %s" % (extract_path)) shutil.rmtree(extract_path) - print - print "Images successfully installed to: %s" % (images_dir) - except Exception, e: - print "Failed to install image archive: %s" % (e) - print "This is usually a permissions problem." - print "Please check your file system access rights and try again." + print("\nImages successfully installed to: %s" % (images_dir)) + except Exception as e: + print("Failed to install image archive: %s" % (e)) + print("This is usually a permissions problem.") + print("Please check your file system access rights and try again.") if options.verbose: traceback.print_exc() else: - print "You can run this again with the '--verbose' flag to see more information" - print "If the problem persists, please email the output to: %s" % (_CONTACT) + print("You can run this again with the '--verbose' flag to see more information") + print("If the problem persists, please email the output to: %s" % (_CONTACT)) else: - print "Checksum of downloaded file is not correct (not installing - see options to override)" - print "Expected: %s" % (options.checksum) - print "Calculated: %s" % (calculated_checksum) - print "Please try downloading again." - print "If the problem persists, please email the output to: %s" % (_CONTACT) + print("Checksum of downloaded file is not correct (not installing - see options to override)") + print("Expected: %s" % (options.checksum)) + print("Calculated: %s" % (calculated_checksum)) + print("Please try downloading again.") + print("If the problem persists, please email the output to: %s" % (_CONTACT)) except KeyboardInterrupt: - print - print "Cancelled at user request" - except Exception, e: - print "Downloader raised an unhandled exception: %s" % (e) + print("\nCancelled at user request") + except Exception as e: + print("Downloader raised an unhandled exception: %s" % (e)) if options.verbose: traceback.print_exc() else: - print "You can run this again with the '--verbose' flag to see more information" - print "If the problem persists, please email the output to: %s" % (_CONTACT) + print("You can run this again with the '--verbose' flag to see more information") + print("If the problem persists, please email the output to: %s" % (_CONTACT)) return 1 return 0 diff --git a/host/utils/usrp_n2xx_net_burner.py b/host/utils/usrp_n2xx_net_burner.py index 8f16de501..5605b0028 100755 --- a/host/utils/usrp_n2xx_net_burner.py +++ b/host/utils/usrp_n2xx_net_burner.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2010-2011 Ettus Research LLC +# Copyright 2010-2011,2015 Ettus Research LLC # # 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 @@ -91,6 +91,45 @@ def seq(): return _seq ######################################################################## +# print equivalent uhd_image_loader command +######################################################################## +def print_image_loader_warning(fw, fpga, reset, safe, addr): + + # Newline + indent + if platform.system() == "Windows": + nl = " ^\n " + else: + nl = " \\\n " + + # Generate uhd_image_loader command based on given arguments + uhd_image_loader = "uhd_image_loader --args=\"type=usrp2,addr={0}".format(addr) + if reset: + uhd_image_loader += ",reset" + if safe: + uhd_image_loader += ",overwrite-safe" + uhd_image_loader += "\"" + + if fw: + uhd_image_loader += "{0}--fw-path=\"{1}\"".format(nl, fw) + else: + uhd_image_loader += "{0}--no-fw".format(nl) + + if fpga: + uhd_image_loader += "{0}--fpga-path=\"{1}\"".format(nl, fpga) + else: + uhd_image_loader += "{0}--no-fpga".format(nl) + + print("") + print("************************************************************************************************") + print("WARNING: This utility will be removed in an upcoming version of UHD. In the future, use") + print(" this command:") + print("") + print(uhd_image_loader) + print("") + print("************************************************************************************************") + print("") + +######################################################################## # helper functions ######################################################################## def unpack_flash_args_fmt(s): @@ -234,6 +273,7 @@ def enumerate_devices(): class burner_socket(object): def __init__(self, addr, quiet): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._addr = addr self._quiet = quiet self._sock.settimeout(UDP_TIMEOUT) self._sock.connect((addr, UDP_FW_UPDATE_PORT)) @@ -284,6 +324,8 @@ class burner_socket(object): return (self.memory_size_bytes, self.sector_size_bytes) def burn_fw(self, fw, fpga, reset, safe, check_rev=True): + print_image_loader_warning(fw, fpga, reset, safe, self._addr) + (flash_size, sector_size) = self.get_flash_info() hw_rev = self.get_hw_rev() @@ -501,7 +543,12 @@ if __name__=='__main__': if options.overwrite_safe and not options.read: print("Are you REALLY, REALLY sure you want to overwrite the safe image? This is ALMOST ALWAYS a terrible idea.") print("If your image is faulty, your USRP2+ will become a brick until reprogrammed via JTAG.") - response = raw_input("""Type "yes" to continue, or anything else to quit: """) + + python_major_version = int(platform.python_version_tuple()[0]) + if python_major_version > 2: + response = input("""Type "yes" to continue, or anything else to quit: """) + else: + response = raw_input("""Type "yes" to continue, or anything else to quit: """) if response != "yes": sys.exit(0) burner = burner_socket(addr=options.addr,quiet=False) diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index 642e9a407..b105e9cb6 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC // // 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 @@ -184,6 +184,59 @@ void list_usrps(){ /*********************************************************************** * Find USRP N2XX with specified IP address and return type **********************************************************************/ +void print_image_loader_warning(const std::string &fw_path, + const std::string &fpga_path, + const po::variables_map &vm){ + + // Newline + indent + #ifdef UHD_PLATFORM_WIN32 + const std::string nl = " ^\n "; + #else + const std::string nl = " \\\n "; + #endif + + std::string uhd_image_loader = str(boost::format("uhd_image_loader --args=\"type=usrp2,addr=%s") + % vm["addr"].as<std::string>()); + if(vm.count("auto-reboot") > 0) + uhd_image_loader += ",reset"; + if(vm.count("overwrite-safe") > 0) + uhd_image_loader += ",overwrite-safe"; + if(vm.count("dont-check-rev") > 0) + uhd_image_loader += ",dont-check-rev"; + + uhd_image_loader += "\""; + + if(vm.count("no-fw") == 0){ + uhd_image_loader += str(boost::format("%s--fw-path=\"%s\"") + % nl % fw_path); + } + else{ + uhd_image_loader += str(boost::format("%s--no-fw") + % nl); + } + + if(vm.count("no-fpga") == 0){ + uhd_image_loader += str(boost::format("%s--fpga-path=\"%s\"") + % nl % fpga_path); + } + else{ + uhd_image_loader += str(boost::format("%s--no-fpga") + % nl); + } + + std::cout << "************************************************************************************************" << std::endl + << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl + << " this command:" << std::endl + << std::endl + << uhd_image_loader << std::endl + << std::endl + << "************************************************************************************************" << std::endl + << std::endl; +} + +/*********************************************************************** + * Find USRP N2XX with specified IP address and return type + **********************************************************************/ boost::uint32_t find_usrp(udp_simple::sptr udp_transport, bool check_rev){ boost::uint32_t hw_rev; bool found_it = false; @@ -627,6 +680,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ fw_image_size = read_fw_image(fw_path); } + print_image_loader_warning(fw_path, fpga_path, vm); + std::cout << "Will burn the following images:" << std::endl; if(burn_fw) std::cout << boost::format(" * Firmware: %s\n") % fw_path; if(burn_fpga) std::cout << boost::format(" * FPGA: %s\n") % fpga_path; diff --git a/host/utils/usrp_x3xx_fpga_burner.cpp b/host/utils/usrp_x3xx_fpga_burner.cpp index 3c6225531..704b291be 100644 --- a/host/utils/usrp_x3xx_fpga_burner.cpp +++ b/host/utils/usrp_x3xx_fpga_burner.cpp @@ -1,5 +1,5 @@ // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-2015 Ettus Research LLC // // 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 @@ -54,7 +54,7 @@ extern "C" { } #define X300_FPGA_BIN_SIZE_BYTES 15877916 -#define X300_FPGA_BIT_MAX_SIZE_BYTES 15878022 +#define X300_FPGA_BIT_MAX_SIZE_BYTES 15878032 #define X300_FPGA_PROG_UDP_PORT 49157 #define X300_FLASH_SECTOR_SIZE 131072 #define X300_PACKET_SIZE_BYTES 256 @@ -195,6 +195,71 @@ void extract_from_lvbitx(std::string lvbitx_path, std::vector<char> &bitstream){ bitstream.swap(decoded_bitstream); } +void print_image_loader_warning(const std::string &fpga_path, const po::variables_map &vm){ + + // Newline + indent + #ifdef UHD_PLATFORM_WIN32 + const std::string nl = " ^\n "; + #else + const std::string nl = " \\\n "; + #endif + + // Generate equivalent uhd_image_loader command + std::string uhd_image_loader = "uhd_image_loader --args=\"type=x300"; + + if(vm.count("addr") > 0){ + uhd_image_loader += str(boost::format(",addr=%s") + % vm["addr"].as<std::string>()); + + if(vm.count("configure") > 0){ + uhd_image_loader += ",configure"; + } + + if(vm.count("verify") > 0){ + uhd_image_loader += ",verify"; + } + } + else{ + uhd_image_loader += str(boost::format(",resource=%s") + % vm["resource"].as<std::string>()); + + /* + * Since we have a default value, vm.count("rpc-port") will + * always be > 0, so only add the option if a different port + * is given. + */ + if(vm["rpc-port"].as<std::string>() != "5444"){ + uhd_image_loader += str(boost::format(",rpc-port=%s") + % vm["rpc-port"].as<std::string>()); + } + } + + if(vm.count("type") > 0){ + uhd_image_loader += str(boost::format(",fpga=%s") + % vm["type"].as<std::string>()); + } + + uhd_image_loader += "\""; + + /* + * The --type option overrides any given path, so only add an FPGA path + * if there was no --type argument. + */ + if(vm.count("type") == 0){ + uhd_image_loader += str(boost::format("%s--fpga-path=\"%s\"") + % nl % fpga_path); + } + + std::cout << "************************************************************************************************" << std::endl + << "WARNING: This utility will be removed in an upcoming version of UHD. In the future, use" << std::endl + << " this command:" << std::endl + << std::endl + << uhd_image_loader << std::endl + << std::endl + << "************************************************************************************************" << std::endl + << std::endl; +} + void ethernet_burn(udp_simple::sptr udp_transport, std::string fpga_path, bool verify){ boost::uint32_t max_size; std::vector<char> bitstream; @@ -479,6 +544,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ throw std::runtime_error("The image filename must end in .bin, .bit, or .lvbitx."); } + print_image_loader_warning(fpga_path, vm); + std::signal(SIGINT, &sig_int_handler); if(vm.count("addr")){ udp_simple::sptr udp_transport = udp_simple::make_connected(ip_addr, BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); diff --git a/tools/kitchen_sink/CMakeLists.txt b/tools/kitchen_sink/CMakeLists.txt index b28a9e411..edbc33bd0 100644 --- a/tools/kitchen_sink/CMakeLists.txt +++ b/tools/kitchen_sink/CMakeLists.txt @@ -17,6 +17,15 @@ cmake_minimum_required(VERSION 2.8) +############################################################################### + +#select the release build type by default to get optimization flags +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") + message(STATUS "Build type not specified: defaulting to release.") +endif(NOT CMAKE_BUILD_TYPE) +set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") + ### Set up build environment ################################################## #### Set up Boost: set(BOOST_REQUIRED_COMPONENTS diff --git a/tools/kitchen_sink/kitchen_sink.cpp b/tools/kitchen_sink/kitchen_sink.cpp index f350f4516..e17ee6437 100644 --- a/tools/kitchen_sink/kitchen_sink.cpp +++ b/tools/kitchen_sink/kitchen_sink.cpp @@ -269,6 +269,33 @@ static void msg_handler(uhd::msg::type_t type, const std::string& msg) } /*********************************************************************** + * Checker thread + **********************************************************************/ + +void check_thread(uhd::usrp::multi_usrp::sptr usrp) +{ + { + std::stringstream ss; + ss << "(" << get_stringified_time() << ") Checker running..." << std::endl; + std::cout << ss.str(); + } + + while (running) + { + uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked"); + if (ref_locked.to_bool() == false) { + std::stringstream ss; + ss << HEADER_WARN"(" << get_stringified_time() << ") " << boost::format("ref_locked: unlocked") << std::endl; + std::cout << ss.str(); + } + + boost::this_thread::sleep(boost::posix_time::seconds(0) + boost::posix_time::microseconds(1000 * 500)); // MAGIC + } + + std::cout << "Checker exiting..." << std::endl; +} + +/*********************************************************************** * Benchmark RX Rate **********************************************************************/ @@ -1511,6 +1538,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } } + if (usrp->get_time_source(0) == "gpsdo") + { + std::cout << boost::format(HEADER "Waiting for GPSDO time to latch") << std::endl; + sleep(1); + } + uhd::time_spec_t time_start = usrp->get_time_now(); // Usually DSP #0 on mboard #0 std::cout << boost::format(HEADER "Time now: %f seconds (%llu ticks)") % time_start.get_real_secs() % time_start.to_ticks(usrp->get_master_clock_rate()) << std::endl; @@ -1755,6 +1788,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ running = true; std::cout << HEADER "Begin..." << std::endl; + thread_group.create_thread(boost::bind(&check_thread, usrp)); + if (tx_channel_nums.size() > 0) tx_thread_begin.wait(l_tx); if (rx_channel_nums.size() > 0) diff --git a/tools/mega_fft/MegaFFT-3.7.grc b/tools/mega_fft/MegaFFT-3.7.grc new file mode 100644 index 000000000..be3b112d4 --- /dev/null +++ b/tools/mega_fft/MegaFFT-3.7.grc @@ -0,0 +1,10459 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.8'?> +<flow_graph> + <timestamp>Thu Feb 19 08:48:00 2015</timestamp> + <block> + <key>options</key> + <param> + <key>author</key> + <value>Ettus Research</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>description</key> + <value>Standard edition, single-channel</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>generate_options</key> + <value>wx_gui</value> + </param> + <param> + <key>id</key> + <value>mega_fft</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value>1</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>title</key> + <value>Mega FFT</value> + </param> + <param> + <key>window_size</key> + <value>2400, 1800</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.get_center_freq()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(173, 1024)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>actual_center_freq</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'tune_obj': tune_obj}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.get_samp_rate()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(762, 1310)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>actual_sample_rate</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'requested_sample_rate': requested_sample_rate}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.get_antennas()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>['']</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1361, 1211)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>antennas</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>if self.fft_sink.controller['average']: self.fft_sink.controller['average'] = False; time.sleep(0.25); self.fft_sink.controller['average'] = True;</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>test</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>none</value> + </param> + <param> + <key>_coordinate</key> + <value>(496, 409)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>any_test_1</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'test': test}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.fft_sink.controller['average'] = True</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>any_test_1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>first_eval</key> + <value>none</value> + </param> + <param> + <key>_coordinate</key> + <value>(617, 499)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>any_test_1_0</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'any_test': any_test_1}</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(529, 632)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>auto_dc_offset_removal</value> + </param> + <param> + <key>label</key> + <value>Auto DC offset removal</value> + </param> + <param> + <key>notebook</key> + <value>nb_analog, 1</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(576, 828)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>auto_iq_correction</value> + </param> + <param> + <key>label</key> + <value>Auto IQ correction</value> + </param> + <param> + <key>notebook</key> + <value>nb_analog, 1</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(505, 1023)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>clicked_freq</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_text_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>clicked_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(423, 921)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>clicked_freq_txt</value> + </param> + <param> + <key>label</key> + <value>Clicked freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>src</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>get_sensor_names</value> + </param> + <param> + <key>_coordinate</key> + <value>(2138, 153)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>daughterboard_sensor_names</value> + </param> + <param> + <key>poll_rate</key> + <value>sensor_interval</value> + </param> + <param> + <key>value</key> + <value>[]</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>'\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_sensor(x))), self.daughterboard_sensor_names))</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>'(none)'</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(2143, 258)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>daughterboard_sensors</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'daughterboard_sensor_names': daughterboard_sensor_names}</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(441, 1338)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>decim</value> + </param> + <param> + <key>value</key> + <value>int(2**decim_idx)</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>int_converter</value> + </param> + <param> + <key>value</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(769, 1062)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>decim</value> + </param> + <param> + <key>label</key> + <value>Divide base sample rate</value> + </param> + <param> + <key>max</key> + <value>max_decim</value> + </param> + <param> + <key>min</key> + <value>1</value> + </param> + <param> + <key>notebook</key> + <value>nb_rate, 0</value> + </param> + <param> + <key>num_steps</key> + <value>max_decim-1</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>int_converter</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(472, 1150)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>decim_idx</value> + </param> + <param> + <key>label</key> + <value>Decimate current sample rate</value> + </param> + <param> + <key>max</key> + <value>int(math.log(max_decim)/math.log(2))</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb_rate, 0</value> + </param> + <param> + <key>num_steps</key> + <value>int(math.log(max_decim)/math.log(2))</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable_any_code_post_eval</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1619, 1219)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dont_need_this_now_built_in_to_Any_Code_block</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>fft_sink</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>win['avg_alpha'] * [1.0/self.fft_sink.win['avg_alpha'],1.0][self.fft_sink.win['average']];</value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 311)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_ave_probe</value> + </param> + <param> + <key>poll_rate</key> + <value>5</value> + </param> + <param> + <key>value</key> + <value>ave</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1322, 397)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_averaging</value> + </param> + <param> + <key>value</key> + <value>str_to_bool(averaging)</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(328, 1177)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_center_freq</value> + </param> + <param> + <key>value</key> + <value>actual_center_freq * relative_freq</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>fft_max_idx_probe</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>level</value> + </param> + <param> + <key>_coordinate</key> + <value>(784, 373)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_idx</value> + </param> + <param> + <key>poll_rate</key> + <value>probe_interval</value> + </param> + <param> + <key>value</key> + <value>(fft_size/2)*0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 682)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_idx_norm</value> + </param> + <param> + <key>value</key> + <value>[fft_max_idx,fft_max_idx-fft_size][int(fft_max_idx>(fft_size/2))]</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>fft_max_lvl_probe</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>level</value> + </param> + <param> + <key>_coordinate</key> + <value>(937, 652)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl</value> + </param> + <param> + <key>poll_rate</key> + <value>probe_interval</value> + </param> + <param> + <key>value</key> + <value>0.0</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>fft_max_lvl_probe_2</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>level</value> + </param> + <param> + <key>_coordinate</key> + <value>(1119, 1682)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_2</value> + </param> + <param> + <key>poll_rate</key> + <value>probe_interval</value> + </param> + <param> + <key>value</key> + <value>0.0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1055, 490)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_log</value> + </param> + <param> + <key>value</key> + <value>math.log10([fft_max_lvl,1.0][int(fft_max_lvl==0.0)])*10.0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(947, 373)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_value</value> + </param> + <param> + <key>value</key> + <value>fft_max_lvl</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1293, 1707)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_value_2</value> + </param> + <param> + <key>value</key> + <value>fft_max_lvl_2</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1155, 399)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_peak_hold</value> + </param> + <param> + <key>value</key> + <value>str_to_bool(peak_hold)</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>((freq <= freq_range.stop()) and (freq >= freq_range.start())) or self.set_requested_freq_txt(self.src.get_center_freq()); print self.src.get_center_freq()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>post</value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 665)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fix_invalid_freq</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(277, 893)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>2, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>freq_fine</value> + </param> + <param> + <key>label</key> + <value>Freq (fine)</value> + </param> + <param> + <key>max</key> + <value>freq_fine_range/2</value> + </param> + <param> + <key>min</key> + <value>-freq_fine_range/2</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + <param> + <key>num_steps</key> + <value>1000</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.get_freq_range()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>uhd.freq_range(freq,freq+1)</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(91, 795)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>freq_range</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>[gain,initial_gain][gain==float('-inf')]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>gain_range.start()</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1467, 1032)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>gain_default</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'initial_gain': initial_gain}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.get_gain_range()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>uhd.gain_range(0,0,0)</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1304, 1034)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>gain_range</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>print self.src.get_antennas()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1361, 1127)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>get_antennas</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>'lo_locked' in self.src.get_sensor_names()</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>False</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(338, 1236)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>has_lo_locked</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>[gain,gain_range.start() + ((gain_range.stop() - gain_range.start()) * 0.25)][gain==float('-inf')]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1380, 839)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>initial_gain</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'gain_range': gain_range}</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1693, 976)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>initial_gain_old</value> + </param> + <param> + <key>value</key> + <value>gain_range.start() + ((gain_range.stop() - gain_range.start()) * 0.25)</value> + </param> + </block> + <block> + <key>variable_text_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>lo_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(162, 729)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>4, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>lo_offset_txt</value> + </param> + <param> + <key>label</key> + <value>LO offset (only for LO offset tuning mode)</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>src</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value>"'lo_locked'"</value> + </param> + <param> + <key>function_name</key> + <value>get_sensor</value> + </param> + <param> + <key>_coordinate</key> + <value>(7, 1282)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>locked_probe</value> + </param> + <param> + <key>poll_rate</key> + <value>lo_check_interval * float(has_lo_locked)</value> + </param> + <param> + <key>value</key> + <value>'(unknown)'</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(947, 489)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>max_bin_freq</value> + </param> + <param> + <key>value</key> + <value>fft_center_freq + (((1.0*fft_max_idx_norm) / fft_size) * samp_rate)</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(947, 430)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>max_bin_freq_old</value> + </param> + <param> + <key>value</key> + <value>fft_center_freq + (((1.0*fft_max_idx_norm - (fft_size/2)) / fft_size) * samp_rate)</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(666, 1121)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>max_decim</value> + </param> + <param> + <key>value</key> + <value>256</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>src</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>get_mboard_sensor_names</value> + </param> + <param> + <key>_coordinate</key> + <value>(1950, 150)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>motherboard_sensor_names</value> + </param> + <param> + <key>poll_rate</key> + <value>sensor_interval</value> + </param> + <param> + <key>value</key> + <value>[]</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>'\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_mboard_sensor(x))), filter(lambda x: x.find('gps') != 0, self.motherboard_sensor_names)))</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>'(none)'</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1954, 257)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>motherboard_sensors</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'motherboard_sensor_names': motherboard_sensor_names}</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>false</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(7, 1154)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>relative_freq</value> + </param> + <param> + <key>label</key> + <value>Baseband Frequency Axis</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>true</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[1, 0]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(179, 1108)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>7, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>relative_freq</value> + </param> + <param> + <key>label</key> + <value>Frequency Axis</value> + </param> + <param> + <key>labels</key> + <value>['RF', 'Baseband']</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>drop_down</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[False, True]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1106, 901)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>relative_time</value> + </param> + <param> + <key>label</key> + <value>Time display</value> + </param> + <param> + <key>labels</key> + <value>['Absolute', 'Relative']</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>drop_down</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(324, 1116)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>requested_freq</value> + </param> + <param> + <key>value</key> + <value>requested_freq_txt + freq_fine</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(91, 706)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>requested_freq_txt</value> + </param> + <param> + <key>label</key> + <value>Requested freq</value> + </param> + <param> + <key>max</key> + <value>[freq_range.stop(), freq_range.start() + 1.0][freq_range.start()==freq_range.stop()]</value> + </param> + <param> + <key>min</key> + <value>freq_range.start()</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + <param> + <key>num_steps</key> + <value>[1000,1][freq_range.start()==freq_range.stop()]</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable_text_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>freq</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(3, 748)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>requested_freq_txt</value> + </param> + <param> + <key>label</key> + <value>Requested base freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(608, 1062)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>requested_sample_rate</value> + </param> + <param> + <key>value</key> + <value>requested_sample_rate_base / (1.*decim)</value> + </param> + </block> + <block> + <key>variable_text_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(588, 1282)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>requested_sample_rate_base</value> + </param> + <param> + <key>label</key> + <value>Requested base sample rate</value> + </param> + <param> + <key>notebook</key> + <value>nb_rate, 0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(498, 1281)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>int(actual_sample_rate)</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[0, 1]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0+1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1442, 4)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>scope_mode</value> + </param> + <param> + <key>label</key> + <value>Scope Mode</value> + </param> + <param> + <key>labels</key> + <value>['Complex', 'Magnitude']</value> + </param> + <param> + <key>notebook</key> + <value>nb, 1</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>radio_buttons</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[0, 1]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0+1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1465, 1432)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>scope_mode_2</value> + </param> + <param> + <key>label</key> + <value>Scope Mode 2</value> + </param> + <param> + <key>labels</key> + <value>['Complex', 'Magnitude']</value> + </param> + <param> + <key>notebook</key> + <value>nb, 1</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>radio_buttons</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>antennas</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>[ [antenna,antennas[0]][antenna not in antennas] ,antennas[0]][antenna=='']</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1526, 1265)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>selected_antenna</value> + </param> + <param> + <key>label</key> + <value>Antenna</value> + </param> + <param> + <key>labels</key> + <value>[antennas,['(default)']][antennas==('',)]</value> + </param> + <param> + <key>notebook</key> + <value>nb_analog, 0</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>radio_buttons</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(187, 5)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>selected_antenna_proxy</value> + </param> + <param> + <key>value</key> + <value>selected_antenna</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>gain_default</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1554, 906)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>selected_gain</value> + </param> + <param> + <key>label</key> + <value>Gain</value> + </param> + <param> + <key>max</key> + <value>[gain_range.stop(), gain_range.start() + 1.0][gain_range.stop()==gain_range.start()]</value> + </param> + <param> + <key>min</key> + <value>gain_range.start()</value> + </param> + <param> + <key>notebook</key> + <value>nb_analog, 0</value> + </param> + <param> + <key>num_steps</key> + <value>[int((abs(gain_range.stop()-gain_range.start())/[gain_range.step(), 1.0][gain_range.step()==0])), 1][gain_range.stop()==gain_range.start()]</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(207, 65)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>selected_gain_proxy</value> + </param> + <param> + <key>value</key> + <value>selected_gain</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.set_antenna(self.selected_antenna)</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>first_eval</key> + <value>none</value> + </param> + <param> + <key>_coordinate</key> + <value>(1526, 1127)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>set_antenna</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'selected_antenna': selected_antenna}</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 411)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 1, 1, 1</value> + </param> + <param> + <key>id</key> + <value>show_max_freq</value> + </param> + <param> + <key>label</key> + <value>Show max freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 538)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 2, 1, 1</value> + </param> + <param> + <key>id</key> + <value>show_max_lvl</value> + </param> + <param> + <key>label</key> + <value>Show max level</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + </block> + <block> + <key>variable_check_box</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>[False, True][show_stream_tags.lower() != 'false']</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1172, 578)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>show_stream_tags_chk</value> + </param> + <param> + <key>label</key> + <value>Print stream tags</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>probe_avg_mag</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>level</value> + </param> + <param> + <key>_coordinate</key> + <value>(765, 820)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>signal_probe</value> + </param> + <param> + <key>poll_rate</key> + <value>probe_interval</value> + </param> + <param> + <key>value</key> + <value>0.0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(916, 911)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>signal_probe_log</value> + </param> + <param> + <key>value</key> + <value>math.log10([signal_probe,1.0][signal_probe==0.0])*10</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>inline</value> + </param> + <param> + <key>code</key> + <value>if self.spec != "": self.src.set_subdev_spec(self.spec, 0); print self.spec</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1795, 618)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>spec_proxy</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>'"' + str(locked_probe) + '"'</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(172, 1234)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>static_locked</value> + </param> + <param> + <key>label</key> + <value>Locked</value> + </param> + <param> + <key>notebook</key> + <value>nb_info, 0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1230, 251)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>str_to_bool</value> + </param> + <param> + <key>value</key> + <value>lambda x: len(x)>0 and x.lower()[0] not in ['0', 'n', 'f']</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>'[' + self.src.get_subdev_spec().strip() + ']'</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>'(unknown)'</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(954, 1201)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>subdev_spec</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[1]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(497, 497)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>test</value> + </param> + <param> + <key>label</key> + <value>Test</value> + </param> + <param> + <key>labels</key> + <value>['Reset FFT average']</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>button</value> + </param> + </block> + <block> + <key>variable_function_probe</key> + <param> + <key>block_id</key> + <value>src</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>function_args</key> + <value></value> + </param> + <param> + <key>function_name</key> + <value>get_time_now</value> + </param> + <param> + <key>_coordinate</key> + <value>(1107, 702)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>time_probe</value> + </param> + <param> + <key>poll_rate</key> + <value>sensor_interval</value> + </param> + <param> + <key>value</key> + <value>uhd.time_spec_t()</value> + </param> + </block> + <block> + <key>variable_chooser</key> + <param> + <key>choices</key> + <value>[0, 1, 2]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>[1,0][lo_offset==0.0]</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 1017)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>tune_mode</value> + </param> + <param> + <key>label</key> + <value>Tune mode</value> + </param> + <param> + <key>labels</key> + <value>['Auto (no LO offset)', 'Auto with LO offset', 'Manual (no DSP)']</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + <param> + <key>style</key> + <value>wx.RA_HORIZONTAL</value> + </param> + <param> + <key>type</key> + <value>drop_down</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 868)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>tune_obj</value> + </param> + <param> + <key>value</key> + <value>[requested_freq, uhd.tune_request(requested_freq, lo_offset_txt), uhd.tune_request(requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][tune_mode]</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.src.set_center_freq(self.tune_obj)</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>uhd.tune_result_t()</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(346, 88)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>tune_result</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'tune_obj': tune_obj}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>inline</value> + </param> + <param> + <key>code</key> + <value>if self.ref != '': self.src.set_clock_source(self.ref, 0); print 'Ref =', self.ref;</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1314, 748)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>update_clock_source</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'ref': ref}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>inline</value> + </param> + <param> + <key>code</key> + <value>if self.pps != '': self.src.set_time_source(self.pps, 0); print '1 PPS =', self.pps;</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1316, 664)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>update_time_source</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'pps': pps}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>'%s (\'%s\'), %s' % (self.src.get_usrp_info().get('mboard_id'), self.src.get_usrp_info().get('mboard_name'), self.src.get_usrp_info().get('rx_subdev_name'))</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>'(unknown)'</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1240, 1201)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>usrp_info</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>',\n'.join(map(lambda x: '%s: %s'%(x,self.src.get_usrp_info().get(x)), self.src.get_usrp_info().keys()))</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>'(unknown)'</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(1096, 1201)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>usrp_info_all</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>[self.src.set_auto_dc_offset(self.auto_dc_offset_removal), self.auto_dc_offset_removal or self.src.set_dc_offset(0+0j)]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(536, 744)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_any_code_auto_dc_offset_removal</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'auto_dc_offset_removal': auto_dc_offset_removal}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.fft_sink.win['average'] = fft_averaging</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>fft_averaging</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>post</value> + </param> + <param> + <key>_coordinate</key> + <value>(354, 227)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_any_code_fft_sink_ave</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'averaging': fft_averaging}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.fft_sink.win['peak_hold'] = fft_peak_hold</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>fft_peak_hold</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(355, 142)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_any_code_fft_sink_peak_hold</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'peak_hold': fft_peak_hold}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>[self.src.set_auto_iq_balance(self.auto_iq_correction), self.auto_iq_correction or self.src.set_iq_balance(0+0j)]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>None</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>inline</value> + </param> + <param> + <key>_coordinate</key> + <value>(583, 940)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_any_code_iq_correction</value> + </param> + <param> + <key>injection</key> + <value>plain</value> + </param> + <param> + <key>deps</key> + <value>{'auto_iq_correction': auto_iq_correction}</value> + </param> + </block> + <block> + <key>variable_any_code</key> + <param> + <key>callback_mode</key> + <value>post</value> + </param> + <param> + <key>code</key> + <value>self.waterfall_sink.win['average'] = fft_averaging</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>value</key> + <value>fft_averaging</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>first_eval</key> + <value>post</value> + </param> + <param> + <key>_coordinate</key> + <value>(171, 643)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_any_code_waterfall_ave</value> + </param> + <param> + <key>injection</key> + <value>variable</value> + </param> + <param> + <key>deps</key> + <value>{'averaging': fft_averaging}</value> + </param> + </block> + <block> + <key>variable_config</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>config_file</key> + <value>default</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1670, 1266)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>variable_config_0</value> + </param> + <param> + <key>option</key> + <value>key</value> + </param> + <param> + <key>section</key> + <value>main</value> + </param> + <param> + <key>type</key> + <value>real</value> + </param> + <param> + <key>writeback</key> + <value>None</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>actual_center_freq</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(343, 724)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_actual_center_freq</value> + </param> + <param> + <key>label</key> + <value>Actual center frequency</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>tune_result.actual_dsp_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(5, 943)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>6, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_actual_dsp_freq</value> + </param> + <param> + <key>label</key> + <value>Actual DSP freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>actual_sample_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(762, 1196)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 1, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_actual_sample_rate</value> + </param> + <param> + <key>label</key> + <value>Actual</value> + </param> + <param> + <key>notebook</key> + <value>nb_rate, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>int_converter</value> + </param> + <param> + <key>value</key> + <value>fft_max_idx</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(779, 473)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_fft_max_idx</value> + </param> + <param> + <key>label</key> + <value>Idx</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>fft_max_lvl</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(948, 552)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_fft_max_lvl_log</value> + </param> + <param> + <key>label</key> + <value>Peak magnitude (dB)</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>window_fn_name_map[window]</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1628, 204)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_fft_window_name</value> + </param> + <param> + <key>label</key> + <value>FFT window</value> + </param> + <param> + <key>notebook</key> + <value>nb_info, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>gain_range.to_pp_string().strip()</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1623, 1032)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_gain_range</value> + </param> + <param> + <key>label</key> + <value>Gain Range</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>signal_probe_log</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(748, 918)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_level_probe</value> + </param> + <param> + <key>label</key> + <value>Signal mag^2 (dB)</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>max_bin_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(767, 579)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>variable_static_max_bin_freq</value> + </param> + <param> + <key>label</key> + <value>Peak freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>requested_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(271, 793)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>3, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_requested_freq</value> + </param> + <param> + <key>label</key> + <value>Requested base + fine freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>value</key> + <value>tune_result.actual_rf_freq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(5, 843)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>5, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_rf_freq</value> + </param> + <param> + <key>label</key> + <value>Actual RF freq</value> + </param> + <param> + <key>notebook</key> + <value>nb_freq, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>subdev_spec</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(955, 1285)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_subdev_spec</value> + </param> + <param> + <key>label</key> + <value>Sub-device</value> + </param> + <param> + <key>notebook</key> + <value>nb_info, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>motherboard_sensors</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1955, 30)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_text_0</value> + </param> + <param> + <key>label</key> + <value>Motherboard</value> + </param> + <param> + <key>notebook</key> + <value>nb, 3</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>daughterboard_sensors</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(2153, 37)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>2, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>variable_static_text_0_0</value> + </param> + <param> + <key>label</key> + <value>Daughterboard</value> + </param> + <param> + <key>notebook</key> + <value>nb, 3</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>str( [time.ctime(time_probe.get_real_secs()), datetime.timedelta(seconds=time_probe.get_real_secs()), time.gmtime(time_probe.get_real_secs())] [relative_time])</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1105, 802)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 1, 1, 2</value> + </param> + <param> + <key>id</key> + <value>variable_static_time_now</value> + </param> + <param> + <key>label</key> + <value>Time now</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + </block> + <block> + <key>variable_static_text</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>converver</key> + <value>str_converter</value> + </param> + <param> + <key>value</key> + <value>usrp_info</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>formatter</key> + <value>None</value> + </param> + <param> + <key>_coordinate</key> + <value>(1123, 1286)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1*10</value> + </param> + <param> + <key>id</key> + <value>variable_static_usrp_info</value> + </param> + <param> + <key>label</key> + <value>USRP</value> + </param> + <param> + <key>notebook</key> + <value>nb_info, 0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1538, 198)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>window_fn</value> + </param> + <param> + <key>value</key> + <value>window_fn_map[window]</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1636, 144)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>window_fn_map</value> + </param> + <param> + <key>value</key> + <value>{'auto': None, 'bh': fft_win.blackmanharris, 'ham': fft_win.hamming, 'han': fft_win.hanning, 'rect': fft_win.rectangular, 'flat': fft_win.flattop}</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1648, 3)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>window_fn_name_map</value> + </param> + <param> + <key>value</key> + <value>{'auto': 'Auto', 'bh': 'Blackman-Harris', 'ham': 'Hamming', 'han': 'Hanning', 'rect': 'Rectangular', 'flat': 'Flattop'}</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1367, 1295)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>antenna</value> + </param> + <param> + <key>label</key> + <value>Antenna (blank for default)</value> + </param> + <param> + <key>short_id</key> + <value>A</value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1635, 519)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>args</value> + </param> + <param> + <key>label</key> + <value>UHD device args</value> + </param> + <param> + <key>short_id</key> + <value>a</value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1525, 399)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>ave</value> + </param> + <param> + <key>label</key> + <value>Average FFT</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>1*0 + 0.5</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1268, 311)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>averaging</value> + </param> + <param> + <key>label</key> + <value>Enable FFT averaging</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>True</value> + </param> + </block> + <block> + <key>baz_time_panel</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>mode</key> + <value>auto</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1106, 1070)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>1, 2, 1, 2</value> + </param> + <param> + <key>id</key> + <value>baz_time_panel_0</value> + </param> + <param> + <key>ignore_next_trigger</key> + <value>['rx_rate', 'rx_freq']</value> + </param> + <param> + <key>item_size</key> + <value>8</value> + </param> + <param> + <key>notebook</key> + <value>nb_test, 0</value> + </param> + <param> + <key>relative</key> + <value>relative_time</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>rate</key> + <value>4</value> + </param> + </block> + <block> + <key>blks2_logpwrfft_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(571, 275)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blks2_logpwrfft_x_0</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>avg_alpha</key> + <value>fft_ave_probe</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>frame_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + </block> + <block> + <key>blks2_selector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1301, 51)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blks2_selector_0</value> + </param> + <param> + <key>input_index</key> + <value>scope_mode</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>num_outputs</key> + <value>1</value> + </param> + <param> + <key>output_index</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blks2_selector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1083, 51)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blks2_selector_0_0</value> + </param> + <param> + <key>input_index</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>num_outputs</key> + <value>2</value> + </param> + <param> + <key>output_index</key> + <value>scope_mode</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blks2_selector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1094, 1423)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blks2_selector_0_0_0</value> + </param> + <param> + <key>input_index</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>num_outputs</key> + <value>2</value> + </param> + <param> + <key>output_index</key> + <value>scope_mode_2</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blks2_selector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1312, 1423)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blks2_selector_0_1</value> + </param> + <param> + <key>input_index</key> + <value>scope_mode_2</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>num_outputs</key> + <value>1</value> + </param> + <param> + <key>output_index</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_argmax_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(793, 259)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_argmax_xx_0</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + </block> + <block> + <key>blocks_argmax_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(804, 1631)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_argmax_xx_0_0</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + </block> + <block> + <key>blocks_complex_to_mag</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1083, 121)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_complex_to_mag_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_complex_to_mag</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1094, 1493)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_complex_to_mag_0_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_complex_to_mag</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(823, 204)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_complex_to_mag_1</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + </block> + <block> + <key>blocks_float_to_complex</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1076, 160)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_float_to_complex_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_float_to_complex</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1087, 1532)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_float_to_complex_0_0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_max_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(792, 326)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_max_xx_0</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + </block> + <block> + <key>blocks_max_xx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(803, 1698)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_max_xx_0_0</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>fft_size</value> + </param> + </block> + <block> + <key>blocks_null_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>bus_conns</key> + <value>[[0,],]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1002, 291)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_null_sink_0</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_null_sink</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>bus_conns</key> + <value>[[0,],]</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1013, 1663)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_null_sink_0_0</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_stream_to_vector</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(572, 132)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_stream_to_vector_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>num_items</key> + <value>fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>blocks_tag_debug</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>display</key> + <value>show_stream_tags_chk</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1266, 521)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>blocks_tag_debug_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>filter</key> + <value>""</value> + </param> + <param> + <key>name</key> + <value></value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1630, 815)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>bw</value> + </param> + <param> + <key>label</key> + <value>Daughterboard bandwidth (where appropriate)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1663, 311)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>dyn_rng</value> + </param> + <param> + <key>label</key> + <value>Dynamic Range</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>130</value> + </param> + </block> + <block> + <key>blocks_probe_signal_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1002, 259)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_idx_probe</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + </block> + <block> + <key>blocks_probe_signal_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1013, 1631)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_idx_probe_0</value> + </param> + <param> + <key>type</key> + <value>short</value> + </param> + </block> + <block> + <key>blocks_probe_signal_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1001, 330)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_probe</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + </block> + <block> + <key>blocks_probe_signal_x</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1012, 1702)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_max_lvl_probe_2</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1425, 400)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_rate</value> + </param> + <param> + <key>label</key> + <value>FFT Rate</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>intx</value> + </param> + <param> + <key>value</key> + <value>15</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1647, 399)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>label</key> + <value>FFT Ref Scale (p2p)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>2.0</value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>fft_size</key> + <value>1024*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>freq_of_interest</key> + <value>float('inf')</value> + </param> + <param> + <key>_coordinate</key> + <value>(162, 176)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>fft_sink</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 0</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>threshold_level</key> + <value>float('inf')</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + <param> + <key>y_divs</key> + <value>int(dyn_rng/10)</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_size</key> + <value>1024*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>freq_of_interest</key> + <value>[None,max_bin_freq][show_max_freq]</value> + </param> + <param> + <key>_coordinate</key> + <value>(215, 258)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>fft_sink_2</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 0</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>threshold_level</key> + <value>[None,fft_max_lvl_value][show_max_lvl]</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + <param> + <key>y_divs</key> + <value>int(dyn_rng/10)</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_size</key> + <value>1024*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>freq_of_interest</key> + <value>float('inf')</value> + </param> + <param> + <key>_coordinate</key> + <value>(363, 1414)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>fft_sink_two</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 0</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>threshold_level</key> + <value>[None,fft_max_lvl_value_2][show_max_lvl]</value> + </param> + <param> + <key>title</key> + <value>FFT Plot 2</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + <param> + <key>y_divs</key> + <value>int(dyn_rng/10)</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_size</key> + <value>1024*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>freq_of_interest</key> + <value>float('inf')</value> + </param> + <param> + <key>_coordinate</key> + <value>(201, 1413)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>fft_sink_two_1</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 0</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>threshold_level</key> + <value>float('inf')</value> + </param> + <param> + <key>title</key> + <value>FFT Plot 2</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + <param> + <key>y_divs</key> + <value>int(dyn_rng/10)</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1429, 312)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_size</value> + </param> + <param> + <key>label</key> + <value>FFT Size</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>intx</value> + </param> + <param> + <key>value</key> + <value>1024</value> + </param> + </block> + <block> + <key>fft_vxx</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>forward</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(571, 176)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>fft_vxx_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>nthreads</key> + <value>1</value> + </param> + <param> + <key>shift</key> + <value>True</value> + </param> + <param> + <key>window</key> + <value>[fft_win.blackmanharris(fft_size),window_fn][int(window_fn!=None)]</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(173, 926)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>freq</value> + </param> + <param> + <key>label</key> + <value>Frequency</value> + </param> + <param> + <key>short_id</key> + <value>f</value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>0 + 100e6</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(415, 830)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>freq_fine_range</value> + </param> + <param> + <key>label</key> + <value>Fine frequency slider range</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>2e6</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1394, 927)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>gain</value> + </param> + <param> + <key>label</key> + <value>Gain (default '-inf' selects relative gain of 25%)</value> + </param> + <param> + <key>short_id</key> + <value>g</value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>float("-inf")</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1310, 928)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>gain_auto</value> + </param> + <param> + <key>label</key> + <value>Gain</value> + </param> + <param> + <key>short_id</key> + <value>g</value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>gain_range.start() + ((gain_range.stop() - gain_range.start()) * 0.25)</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1532, 258)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>import</key> + <value>from gnuradio.fft import window as fft_win</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1271, 849)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>import_1</value> + </param> + <param> + <key>import</key> + <value>import time, datetime</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1, 703)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>import_math</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(172, 1324)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>lo_check_interval</value> + </param> + <param> + <key>label</key> + <value>LO lock check frequency (Hz)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>5</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(339, 1024)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>lo_offset</value> + </param> + <param> + <key>label</key> + <value>LO offset (selects LO offset tuning mode)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + </block> + <block> + <key>logpwrfft_x</key> + <param> + <key>avg_alpha</key> + <value>fft_ave_probe</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>frame_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>_coordinate</key> + <value>(574, 278)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>logpwrfft_x_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + </block> + <block> + <key>logpwrfft_x</key> + <param> + <key>avg_alpha</key> + <value>fft_ave_probe</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>frame_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>_coordinate</key> + <value>(585, 1650)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>logpwrfft_x_0_0</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>sample_rate</key> + <value>samp_rate</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(913, 825)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>mag_alpha</value> + </param> + <param> + <key>label</key> + <value>Signal magnitude probe alpha</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>1e-3</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 82)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>nb</value> + </param> + <param> + <key>labels</key> + <value>['FFT', 'Scope', 'Waterfall', 'Sensors']</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>style</key> + <value>wx.NB_TOP</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(6, 352)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>nb_analog</value> + </param> + <param> + <key>labels</key> + <value>['Analog', 'DC']</value> + </param> + <param> + <key>notebook</key> + <value>nb_right, 0</value> + </param> + <param> + <key>style</key> + <value>wx.NB_LEFT</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(4, 532)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>nb_freq</value> + </param> + <param> + <key>labels</key> + <value>['Freq']</value> + </param> + <param> + <key>notebook</key> + <value>nb_right, 0</value> + </param> + <param> + <key>style</key> + <value>wx.NB_LEFT</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(6, 168)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 0, 1, 1</value> + </param> + <param> + <key>id</key> + <value>nb_info</value> + </param> + <param> + <key>labels</key> + <value>['Info']</value> + </param> + <param> + <key>notebook</key> + <value>nb_right, 0</value> + </param> + <param> + <key>style</key> + <value>wx.NB_LEFT</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(5, 615)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>nb_rate</value> + </param> + <param> + <key>labels</key> + <value>['Rate']</value> + </param> + <param> + <key>notebook</key> + <value>nb_right, 0</value> + </param> + <param> + <key>style</key> + <value>wx.NB_LEFT</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(6, 265)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value>0, 1, 1, 1</value> + </param> + <param> + <key>id</key> + <value>nb_right</value> + </param> + <param> + <key>labels</key> + <value>['Params']</value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>style</key> + <value>wx.NB_TOP</value> + </param> + </block> + <block> + <key>notebook</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(6, 442)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>nb_test</value> + </param> + <param> + <key>labels</key> + <value>['Test']</value> + </param> + <param> + <key>notebook</key> + <value>nb_right, 0</value> + </param> + <param> + <key>style</key> + <value>wx.NB_LEFT</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1146, 312)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>peak_hold</value> + </param> + <param> + <key>label</key> + <value>FFT peak hold</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>False</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1630, 730)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>pps</value> + </param> + <param> + <key>label</key> + <value>Time source (none, internal, external, mimo, gpsdo). <empty> leaves it at the default.</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>''</value> + </param> + </block> + <block> + <key>analog_probe_avg_mag_sqrd_x</key> + <param> + <key>alpha</key> + <value>mag_alpha</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(945, 768)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>probe_avg_mag</value> + </param> + <param> + <key>threshold</key> + <value>0</value> + </param> + <param> + <key>type</key> + <value>c</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(908, 967)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>probe_interval</value> + </param> + <param> + <key>label</key> + <value>Signal probe frequency (Hz)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>3</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(644, 1183)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>rate</value> + </param> + <param> + <key>label</key> + <value>Sample Rate</value> + </param> + <param> + <key>short_id</key> + <value>s</value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>1e6</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1467, 730)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>ref</value> + </param> + <param> + <key>label</key> + <value>Clock source (internal, external, mimo, gpsdo). <empty> leaves it at the default.</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>''</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1527, 311)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>ref_lvl</value> + </param> + <param> + <key>label</key> + <value>Reference Level</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(936, 1087)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>sensor_interval</value> + </param> + <param> + <key>label</key> + <value>Sensor update frequency (Hz)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>eng_float</value> + </param> + <param> + <key>value</key> + <value>2</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1323, 580)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>show_stream_tags</value> + </param> + <param> + <key>label</key> + <value>Print stream tags</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>False</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1634, 618)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>spec</value> + </param> + <param> + <key>label</key> + <value>Sub-device specification (where appropriate)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>''</value> + </param> + </block> + <block> + <key>uhd_usrp_source</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value>selected_antenna_proxy</value> + </param> + <param> + <key>bw0</key> + <value>bw</value> + </param> + <param> + <key>center_freq0</key> + <value>uhd.tune_request(freq, lo_offset)</value> + </param> + <param> + <key>norm_gain0</key> + <value>False</value> + </param> + <param> + <key>gain0</key> + <value>selected_gain_proxy</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>center_freq10</key> + <value>0</value> + </param> + <param> + <key>norm_gain10</key> + <value>False</value> + </param> + <param> + <key>gain10</key> + <value>0</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>center_freq11</key> + <value>0</value> + </param> + <param> + <key>norm_gain11</key> + <value>False</value> + </param> + <param> + <key>gain11</key> + <value>0</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>center_freq12</key> + <value>0</value> + </param> + <param> + <key>norm_gain12</key> + <value>False</value> + </param> + <param> + <key>gain12</key> + <value>0</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>center_freq13</key> + <value>0</value> + </param> + <param> + <key>norm_gain13</key> + <value>False</value> + </param> + <param> + <key>gain13</key> + <value>0</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>center_freq14</key> + <value>0</value> + </param> + <param> + <key>norm_gain14</key> + <value>False</value> + </param> + <param> + <key>gain14</key> + <value>0</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>center_freq15</key> + <value>0</value> + </param> + <param> + <key>norm_gain15</key> + <value>False</value> + </param> + <param> + <key>gain15</key> + <value>0</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>center_freq16</key> + <value>0</value> + </param> + <param> + <key>norm_gain16</key> + <value>False</value> + </param> + <param> + <key>gain16</key> + <value>0</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>center_freq17</key> + <value>0</value> + </param> + <param> + <key>norm_gain17</key> + <value>False</value> + </param> + <param> + <key>gain17</key> + <value>0</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>center_freq18</key> + <value>0</value> + </param> + <param> + <key>norm_gain18</key> + <value>False</value> + </param> + <param> + <key>gain18</key> + <value>0</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>center_freq19</key> + <value>0</value> + </param> + <param> + <key>norm_gain19</key> + <value>False</value> + </param> + <param> + <key>gain19</key> + <value>0</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>center_freq1</key> + <value>0</value> + </param> + <param> + <key>norm_gain1</key> + <value>False</value> + </param> + <param> + <key>gain1</key> + <value>0</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>center_freq20</key> + <value>0</value> + </param> + <param> + <key>norm_gain20</key> + <value>False</value> + </param> + <param> + <key>gain20</key> + <value>0</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>center_freq21</key> + <value>0</value> + </param> + <param> + <key>norm_gain21</key> + <value>False</value> + </param> + <param> + <key>gain21</key> + <value>0</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>center_freq22</key> + <value>0</value> + </param> + <param> + <key>norm_gain22</key> + <value>False</value> + </param> + <param> + <key>gain22</key> + <value>0</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>center_freq23</key> + <value>0</value> + </param> + <param> + <key>norm_gain23</key> + <value>False</value> + </param> + <param> + <key>gain23</key> + <value>0</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>center_freq24</key> + <value>0</value> + </param> + <param> + <key>norm_gain24</key> + <value>False</value> + </param> + <param> + <key>gain24</key> + <value>0</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>center_freq25</key> + <value>0</value> + </param> + <param> + <key>norm_gain25</key> + <value>False</value> + </param> + <param> + <key>gain25</key> + <value>0</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>center_freq26</key> + <value>0</value> + </param> + <param> + <key>norm_gain26</key> + <value>False</value> + </param> + <param> + <key>gain26</key> + <value>0</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>center_freq27</key> + <value>0</value> + </param> + <param> + <key>norm_gain27</key> + <value>False</value> + </param> + <param> + <key>gain27</key> + <value>0</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>center_freq28</key> + <value>0</value> + </param> + <param> + <key>norm_gain28</key> + <value>False</value> + </param> + <param> + <key>gain28</key> + <value>0</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>center_freq29</key> + <value>0</value> + </param> + <param> + <key>norm_gain29</key> + <value>False</value> + </param> + <param> + <key>gain29</key> + <value>0</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>center_freq2</key> + <value>0</value> + </param> + <param> + <key>norm_gain2</key> + <value>False</value> + </param> + <param> + <key>gain2</key> + <value>0</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>center_freq30</key> + <value>0</value> + </param> + <param> + <key>norm_gain30</key> + <value>False</value> + </param> + <param> + <key>gain30</key> + <value>0</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>center_freq31</key> + <value>0</value> + </param> + <param> + <key>norm_gain31</key> + <value>False</value> + </param> + <param> + <key>gain31</key> + <value>0</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>center_freq3</key> + <value>0</value> + </param> + <param> + <key>norm_gain3</key> + <value>False</value> + </param> + <param> + <key>gain3</key> + <value>0</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>center_freq4</key> + <value>0</value> + </param> + <param> + <key>norm_gain4</key> + <value>False</value> + </param> + <param> + <key>gain4</key> + <value>0</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>center_freq5</key> + <value>0</value> + </param> + <param> + <key>norm_gain5</key> + <value>False</value> + </param> + <param> + <key>gain5</key> + <value>0</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>center_freq6</key> + <value>0</value> + </param> + <param> + <key>norm_gain6</key> + <value>False</value> + </param> + <param> + <key>gain6</key> + <value>0</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>center_freq7</key> + <value>0</value> + </param> + <param> + <key>norm_gain7</key> + <value>False</value> + </param> + <param> + <key>gain7</key> + <value>0</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>center_freq8</key> + <value>0</value> + </param> + <param> + <key>norm_gain8</key> + <value>False</value> + </param> + <param> + <key>gain8</key> + <value>0</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>center_freq9</key> + <value>0</value> + </param> + <param> + <key>norm_gain9</key> + <value>False</value> + </param> + <param> + <key>gain9</key> + <value>0</value> + </param> + <param> + <key>clock_rate</key> + <value>0.0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>dev_addr</key> + <value>args</value> + </param> + <param> + <key>dev_args</key> + <value>""</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(344, 4)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>src</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>sd_spec0</key> + <value>spec</value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>sd_spec1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>sd_spec2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>sd_spec3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>sd_spec4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>sd_spec5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>sd_spec6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>sd_spec7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>samp_rate</key> + <value>requested_sample_rate</value> + </param> + <param> + <key>stream_args</key> + <value>stream_args</value> + </param> + <param> + <key>stream_chans</key> + <value>[]</value> + </param> + <param> + <key>stream_immediately</key> + <value>True</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + <param> + <key>otw</key> + <value>wire_format</value> + </param> + </block> + <block> + <key>uhd_usrp_source</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>ant0</key> + <value>selected_antenna_proxy</value> + </param> + <param> + <key>bw0</key> + <value>bw</value> + </param> + <param> + <key>center_freq0</key> + <value>uhd.tune_request(freq, lo_offset)</value> + </param> + <param> + <key>norm_gain0</key> + <value>False</value> + </param> + <param> + <key>gain0</key> + <value>selected_gain_proxy</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>center_freq10</key> + <value>0</value> + </param> + <param> + <key>norm_gain10</key> + <value>False</value> + </param> + <param> + <key>gain10</key> + <value>0</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>center_freq11</key> + <value>0</value> + </param> + <param> + <key>norm_gain11</key> + <value>False</value> + </param> + <param> + <key>gain11</key> + <value>0</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>center_freq12</key> + <value>0</value> + </param> + <param> + <key>norm_gain12</key> + <value>False</value> + </param> + <param> + <key>gain12</key> + <value>0</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>center_freq13</key> + <value>0</value> + </param> + <param> + <key>norm_gain13</key> + <value>False</value> + </param> + <param> + <key>gain13</key> + <value>0</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>center_freq14</key> + <value>0</value> + </param> + <param> + <key>norm_gain14</key> + <value>False</value> + </param> + <param> + <key>gain14</key> + <value>0</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>center_freq15</key> + <value>0</value> + </param> + <param> + <key>norm_gain15</key> + <value>False</value> + </param> + <param> + <key>gain15</key> + <value>0</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>center_freq16</key> + <value>0</value> + </param> + <param> + <key>norm_gain16</key> + <value>False</value> + </param> + <param> + <key>gain16</key> + <value>0</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>center_freq17</key> + <value>0</value> + </param> + <param> + <key>norm_gain17</key> + <value>False</value> + </param> + <param> + <key>gain17</key> + <value>0</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>center_freq18</key> + <value>0</value> + </param> + <param> + <key>norm_gain18</key> + <value>False</value> + </param> + <param> + <key>gain18</key> + <value>0</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>center_freq19</key> + <value>0</value> + </param> + <param> + <key>norm_gain19</key> + <value>False</value> + </param> + <param> + <key>gain19</key> + <value>0</value> + </param> + <param> + <key>ant1</key> + <value>selected_antenna_proxy</value> + </param> + <param> + <key>bw1</key> + <value>bw</value> + </param> + <param> + <key>center_freq1</key> + <value>uhd.tune_request(freq, lo_offset)</value> + </param> + <param> + <key>norm_gain1</key> + <value>False</value> + </param> + <param> + <key>gain1</key> + <value>selected_gain_proxy</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>center_freq20</key> + <value>0</value> + </param> + <param> + <key>norm_gain20</key> + <value>False</value> + </param> + <param> + <key>gain20</key> + <value>0</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>center_freq21</key> + <value>0</value> + </param> + <param> + <key>norm_gain21</key> + <value>False</value> + </param> + <param> + <key>gain21</key> + <value>0</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>center_freq22</key> + <value>0</value> + </param> + <param> + <key>norm_gain22</key> + <value>False</value> + </param> + <param> + <key>gain22</key> + <value>0</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>center_freq23</key> + <value>0</value> + </param> + <param> + <key>norm_gain23</key> + <value>False</value> + </param> + <param> + <key>gain23</key> + <value>0</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>center_freq24</key> + <value>0</value> + </param> + <param> + <key>norm_gain24</key> + <value>False</value> + </param> + <param> + <key>gain24</key> + <value>0</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>center_freq25</key> + <value>0</value> + </param> + <param> + <key>norm_gain25</key> + <value>False</value> + </param> + <param> + <key>gain25</key> + <value>0</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>center_freq26</key> + <value>0</value> + </param> + <param> + <key>norm_gain26</key> + <value>False</value> + </param> + <param> + <key>gain26</key> + <value>0</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>center_freq27</key> + <value>0</value> + </param> + <param> + <key>norm_gain27</key> + <value>False</value> + </param> + <param> + <key>gain27</key> + <value>0</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>center_freq28</key> + <value>0</value> + </param> + <param> + <key>norm_gain28</key> + <value>False</value> + </param> + <param> + <key>gain28</key> + <value>0</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>center_freq29</key> + <value>0</value> + </param> + <param> + <key>norm_gain29</key> + <value>False</value> + </param> + <param> + <key>gain29</key> + <value>0</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>center_freq2</key> + <value>0</value> + </param> + <param> + <key>norm_gain2</key> + <value>False</value> + </param> + <param> + <key>gain2</key> + <value>0</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>center_freq30</key> + <value>0</value> + </param> + <param> + <key>norm_gain30</key> + <value>False</value> + </param> + <param> + <key>gain30</key> + <value>0</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bw31</key> + <value>0</value> + </param> + <param> + <key>center_freq31</key> + <value>0</value> + </param> + <param> + <key>norm_gain31</key> + <value>False</value> + </param> + <param> + <key>gain31</key> + <value>0</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>center_freq3</key> + <value>0</value> + </param> + <param> + <key>norm_gain3</key> + <value>False</value> + </param> + <param> + <key>gain3</key> + <value>0</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>center_freq4</key> + <value>0</value> + </param> + <param> + <key>norm_gain4</key> + <value>False</value> + </param> + <param> + <key>gain4</key> + <value>0</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>center_freq5</key> + <value>0</value> + </param> + <param> + <key>norm_gain5</key> + <value>False</value> + </param> + <param> + <key>gain5</key> + <value>0</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>center_freq6</key> + <value>0</value> + </param> + <param> + <key>norm_gain6</key> + <value>False</value> + </param> + <param> + <key>gain6</key> + <value>0</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>center_freq7</key> + <value>0</value> + </param> + <param> + <key>norm_gain7</key> + <value>False</value> + </param> + <param> + <key>gain7</key> + <value>0</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>center_freq8</key> + <value>0</value> + </param> + <param> + <key>norm_gain8</key> + <value>False</value> + </param> + <param> + <key>gain8</key> + <value>0</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>center_freq9</key> + <value>0</value> + </param> + <param> + <key>norm_gain9</key> + <value>False</value> + </param> + <param> + <key>gain9</key> + <value>0</value> + </param> + <param> + <key>clock_rate</key> + <value>0.0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>dev_addr</key> + <value>args</value> + </param> + <param> + <key>dev_args</key> + <value>""</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(344, 50)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>src_2</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>sd_spec0</key> + <value>spec</value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>sd_spec1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>sd_spec2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>sd_spec3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>sd_spec4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>sd_spec5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>sd_spec6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>sd_spec7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>nchan</key> + <value>2</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>samp_rate</key> + <value>requested_sample_rate</value> + </param> + <param> + <key>stream_args</key> + <value>stream_args</value> + </param> + <param> + <key>stream_chans</key> + <value>[]</value> + </param> + <param> + <key>stream_immediately</key> + <value>True</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + <param> + <key>otw</key> + <value>wire_format</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1462, 617)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>stream_args</value> + </param> + <param> + <key>label</key> + <value>Stream arguments (e.g. scalar=1024)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>virtual_sink</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(624, 4)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_sink_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_sink</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(624, 45)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_sink_0_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb2</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1246, 478)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1081, 5)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(173, 128)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_0_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(760, 775)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_0_0_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1106, 1027)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_1</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(571, 88)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_2</value> + </param> + <param> + <key>stream_id</key> + <value>bb</value> + </param> + </block> + <block> + <key>virtual_source</key> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(27, 1456)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>virtual_source_0_2_0</value> + </param> + <param> + <key>stream_id</key> + <value>bb2</value> + </param> + </block> + <block> + <key>wxgui_waterfallsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>dynamic_range</key> + <value>dyn_rng</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>fft_size</key> + <value>512*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>_coordinate</key> + <value>(177, 458)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>waterfall_sink</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 2</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>title</key> + <value>Waterfall Plot</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + </block> + <block> + <key>wxgui_waterfallsink2</key> + <param> + <key>avg_alpha</key> + <value>ave</value> + </param> + <param> + <key>average</key> + <value>True</value> + </param> + <param> + <key>baseband_freq</key> + <value>fft_center_freq</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>dynamic_range</key> + <value>dyn_rng</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>fft_rate</key> + <value>fft_rate</value> + </param> + <param> + <key>fft_size</key> + <value>512*0 + fft_size</value> + </param> + <param> + <key>freqvar</key> + <value>clicked_freq</value> + </param> + <param> + <key>_coordinate</key> + <value>(181, 1616)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>waterfall_sink_0</value> + </param> + <param> + <key>fft_in</key> + <value>False</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 2</value> + </param> + <param> + <key>fft_out</key> + <value>False</value> + </param> + <param> + <key>ref_scale</key> + <value>fft_ref_scale</value> + </param> + <param> + <key>ref_level</key> + <value>ref_lvl</value> + </param> + <param> + <key>always_run</key> + <value>False</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>title</key> + <value>Waterfall Plot 2</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>win</key> + <value>window_fn</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1641, 60)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>window</value> + </param> + <param> + <key>label</key> + <value>Window (bh: Blackman-Harris, ham: Hamming, han: Hanning, rect: Rectangular, flat: Flattop)</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value>auto</value> + </param> + </block> + <block> + <key>parameter</key> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1471, 531)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>id</key> + <value>wire_format</value> + </param> + <param> + <key>label</key> + <value>Wire format (e.g. sc16, sc8) [blank = automatic]</value> + </param> + <param> + <key>short_id</key> + <value></value> + </param> + <param> + <key>type</key> + <value>string</value> + </param> + <param> + <key>value</key> + <value></value> + </param> + </block> + <block> + <key>wxgui_scopesink2</key> + <param> + <key>ac_couple</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(1291, 119)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>wxgui_scopesink2_0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 1</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>t_scale</key> + <value>0</value> + </param> + <param> + <key>title</key> + <value>Scope Plot</value> + </param> + <param> + <key>trig_mode</key> + <value>wxgui.TRIG_MODE_AUTO</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>v_offset</key> + <value>0</value> + </param> + <param> + <key>v_scale</key> + <value>0</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>xy_mode</key> + <value>False</value> + </param> + <param> + <key>y_axis_label</key> + <value>Counts</value> + </param> + </block> + <block> + <key>wxgui_scopesink2</key> + <param> + <key>ac_couple</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(1302, 1491)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>id</key> + <value>wxgui_scopesink2_0_0</value> + </param> + <param> + <key>notebook</key> + <value>nb, 1</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>t_scale</key> + <value>0</value> + </param> + <param> + <key>title</key> + <value>Scope Plot 2</value> + </param> + <param> + <key>trig_mode</key> + <value>wxgui.TRIG_MODE_AUTO</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>v_offset</key> + <value>0</value> + </param> + <param> + <key>v_scale</key> + <value>0</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>xy_mode</key> + <value>False</value> + </param> + <param> + <key>y_axis_label</key> + <value>Counts</value> + </param> + </block> + <connection> + <source_block_id>blks2_logpwrfft_x_0</source_block_id> + <sink_block_id>blocks_argmax_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_logpwrfft_x_0</source_block_id> + <sink_block_id>blocks_max_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0</source_block_id> + <sink_block_id>wxgui_scopesink2_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0_0</source_block_id> + <sink_block_id>blks2_selector_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0_0</source_block_id> + <sink_block_id>blocks_complex_to_mag_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0_0_0</source_block_id> + <sink_block_id>blks2_selector_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0_0_0</source_block_id> + <sink_block_id>blocks_complex_to_mag_0_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blks2_selector_0_1</source_block_id> + <sink_block_id>wxgui_scopesink2_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_argmax_xx_0</source_block_id> + <sink_block_id>blocks_null_sink_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_argmax_xx_0</source_block_id> + <sink_block_id>fft_max_idx_probe</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_argmax_xx_0_0</source_block_id> + <sink_block_id>blocks_null_sink_0_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_argmax_xx_0_0</source_block_id> + <sink_block_id>fft_max_idx_probe_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_0_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_0_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_1</source_block_id> + <sink_block_id>blocks_argmax_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_complex_to_mag_1</source_block_id> + <sink_block_id>blocks_max_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_complex_0</source_block_id> + <sink_block_id>blks2_selector_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_complex_0_0</source_block_id> + <sink_block_id>blks2_selector_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_max_xx_0</source_block_id> + <sink_block_id>fft_max_lvl_probe</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_max_xx_0_0</source_block_id> + <sink_block_id>fft_max_lvl_probe_2</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_stream_to_vector_0</source_block_id> + <sink_block_id>fft_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_vxx_0</source_block_id> + <sink_block_id>blocks_complex_to_mag_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>logpwrfft_x_0</source_block_id> + <sink_block_id>blocks_argmax_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>logpwrfft_x_0</source_block_id> + <sink_block_id>blocks_max_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>logpwrfft_x_0_0</source_block_id> + <sink_block_id>blocks_argmax_xx_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>logpwrfft_x_0_0</source_block_id> + <sink_block_id>blocks_max_xx_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>src</source_block_id> + <sink_block_id>virtual_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>src_2</source_block_id> + <sink_block_id>virtual_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>src_2</source_block_id> + <sink_block_id>virtual_sink_0_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0</source_block_id> + <sink_block_id>blocks_tag_debug_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_0</source_block_id> + <sink_block_id>blks2_selector_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_0_0</source_block_id> + <sink_block_id>fft_sink</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_0_0</source_block_id> + <sink_block_id>fft_sink_2</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_0_0</source_block_id> + <sink_block_id>waterfall_sink</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_0_0_0</source_block_id> + <sink_block_id>probe_avg_mag</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_1</source_block_id> + <sink_block_id>baz_time_panel_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2</source_block_id> + <sink_block_id>blks2_logpwrfft_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2</source_block_id> + <sink_block_id>blocks_stream_to_vector_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2</source_block_id> + <sink_block_id>logpwrfft_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2_0</source_block_id> + <sink_block_id>blks2_selector_0_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2_0</source_block_id> + <sink_block_id>fft_sink_two</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2_0</source_block_id> + <sink_block_id>fft_sink_two_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2_0</source_block_id> + <sink_block_id>logpwrfft_x_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>virtual_source_0_2_0</source_block_id> + <sink_block_id>waterfall_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/tools/mega_fft/README.md b/tools/mega_fft/README.md new file mode 100644 index 000000000..c61207ec7 --- /dev/null +++ b/tools/mega_fft/README.md @@ -0,0 +1,7 @@ +== MegaFFT == + +Highly configurable tool similar to `uhd_fft`. + +Requirements: +* gr-baz required for GRC version + diff --git a/tools/mega_fft/mega_fft.py b/tools/mega_fft/mega_fft.py new file mode 100755 index 000000000..571653d2f --- /dev/null +++ b/tools/mega_fft/mega_fft.py @@ -0,0 +1,2646 @@ +#!/usr/bin/env python2 +################################################## +# GNU Radio Python Flow Graph +# Copyright 2014 National Instruments +# +# Title: Mega FFT +# Description: Standard edition, single-channel +# Generated: Fri Jul 24 11:56:33 2015 +################################################## + +if __name__ == '__main__': + import ctypes + import sys + if sys.platform.startswith('linux'): + try: + x11 = ctypes.cdll.LoadLibrary('libX11.so') + x11.XInitThreads() + except: + print "Warning: failed to XInitThreads()" + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import gr +from gnuradio import uhd +from gnuradio import wxgui +from gnuradio.eng_option import eng_option +from gnuradio.fft import logpwrfft +from gnuradio.fft import window +from gnuradio.fft import window as fft_win +from gnuradio.filter import firdes +from gnuradio.wxgui import fftsink2 +from gnuradio.wxgui import forms +from gnuradio.wxgui import scopesink2 +from gnuradio.wxgui import waterfallsink2 +from grc_gnuradio import blks2 as grc_blks2 +from grc_gnuradio import wxgui as grc_wxgui +from optparse import OptionParser +import math +import threading +import time +import time, datetime +import wx + +class mega_fft(grc_wxgui.top_block_gui): + + def __init__(self, antenna="", args="", ave=1*0 + 0.5, averaging="True", bw=0, dyn_rng=130, fft_rate=15, fft_ref_scale=2.0, fft_size=1024, freq=0 + 100e6, freq_fine_range=2e6, gain=float("-inf"), lo_check_interval=5, lo_offset=0, mag_alpha=1e-3, peak_hold="False", pps='', probe_interval=3, rate=1e6, ref='', ref_lvl=0, sensor_interval=2, show_stream_tags="False", spec='', stream_args="", window="auto", wire_format=""): + grc_wxgui.top_block_gui.__init__(self, title="Mega FFT") + + ################################################## + # Parameters + ################################################## + self.antenna = antenna + self.args = args + self.ave = ave + self.averaging = averaging + self.bw = bw + self.dyn_rng = dyn_rng + self.fft_rate = fft_rate + self.fft_ref_scale = fft_ref_scale + self.fft_size = fft_size + self.freq = freq + self.freq_fine_range = freq_fine_range + self.gain = gain + self.lo_check_interval = lo_check_interval + self.lo_offset = lo_offset + self.mag_alpha = mag_alpha + self.peak_hold = peak_hold + self.pps = pps + self.probe_interval = probe_interval + self.rate = rate + self.ref = ref + self.ref_lvl = ref_lvl + self.sensor_interval = sensor_interval + self.show_stream_tags = show_stream_tags + self.spec = spec + self.stream_args = stream_args + self.window = window + self.wire_format = wire_format + + ################################################## + # Variables + ################################################## + self.relative_freq = relative_freq = 1 + self.gain_range = gain_range = uhd.gain_range(0,0,0) + self.fft_max_idx = fft_max_idx = (fft_size/2)*0 + self.actual_sample_rate = actual_sample_rate = rate + self.actual_center_freq = actual_center_freq = 0 + self.str_to_bool = str_to_bool = lambda x: len(x)>0 and x.lower()[0] not in ['0', 'n', 'f'] + self.signal_probe = signal_probe = 0.0 + self.samp_rate = samp_rate = int(actual_sample_rate) + self.requested_freq_txt = requested_freq_txt = freq + self.gain_default = gain_default = gain_range.start() + self.freq_fine = freq_fine = 0 + self.fft_max_idx_norm = fft_max_idx_norm = [fft_max_idx,fft_max_idx-fft_size][int(fft_max_idx>(fft_size/2))] + self.fft_center_freq = fft_center_freq = actual_center_freq * relative_freq + self.antennas = antennas = [''] + self.window_fn_name_map = window_fn_name_map = {'auto': 'Auto', 'bh': 'Blackman-Harris', 'ham': 'Hamming', 'han': 'Hanning', 'rect': 'Rectangular', 'flat': 'Flattop'} + self.window_fn_map = window_fn_map = {'auto': None, 'bh': fft_win.blackmanharris, 'ham': fft_win.hamming, 'han': fft_win.hanning, 'rect': fft_win.rectangular, 'flat': fft_win.flattop} + self.usrp_info = usrp_info = '(unknown)' + self.tune_result = tune_result = uhd.tune_result_t() + self.tune_mode = tune_mode = [1,0][lo_offset==0.0] + self.time_probe = time_probe = uhd.time_spec_t() + self.test = test = 1 + self.subdev_spec = subdev_spec = '(unknown)' + self.signal_probe_log = signal_probe_log = math.log10([signal_probe,1.0][signal_probe==0.0])*10 + self.selected_gain = selected_gain = gain_default + self.selected_antenna = selected_antenna = [ [antenna,antennas[0]][antenna not in antennas] ,antennas[0]][antenna==''] + self.requested_sample_rate_base = requested_sample_rate_base = rate + self.requested_freq = requested_freq = requested_freq_txt + freq_fine + self.relative_time = relative_time = True + self.motherboard_sensors = motherboard_sensors = '(none)' + self.max_bin_freq = max_bin_freq = fft_center_freq + (((1.0*fft_max_idx_norm) / fft_size) * samp_rate) + self.locked_probe = locked_probe = '(unknown)' + self.lo_offset_txt = lo_offset_txt = lo_offset + self.fft_peak_hold = fft_peak_hold = str_to_bool(peak_hold) + self.fft_max_lvl = fft_max_lvl = 0.0 + self.fft_averaging = fft_averaging = str_to_bool(averaging) + self.decim = decim = 1 + self.daughterboard_sensors = daughterboard_sensors = '(none)' + self.clicked_freq = clicked_freq = 0 + self.window_fn = window_fn = window_fn_map[window] + self.variable_static_usrp_info = variable_static_usrp_info = usrp_info + self.variable_static_time_now = variable_static_time_now = str( [time.ctime(time_probe.get_real_secs()), datetime.timedelta(seconds=time_probe.get_real_secs()), time.gmtime(time_probe.get_real_secs())] [relative_time]) + self.variable_static_text_0_0 = variable_static_text_0_0 = daughterboard_sensors + self.variable_static_text_0 = variable_static_text_0 = motherboard_sensors + self.variable_static_subdev_spec = variable_static_subdev_spec = subdev_spec + self.variable_static_rf_freq = variable_static_rf_freq = tune_result.actual_rf_freq + self.variable_static_requested_freq = variable_static_requested_freq = requested_freq + self.variable_static_max_bin_freq = variable_static_max_bin_freq = max_bin_freq + self.variable_static_level_probe = variable_static_level_probe = signal_probe_log + self.variable_static_fft_window_name = variable_static_fft_window_name = window_fn_name_map[window] + self.variable_static_fft_max_lvl_log = variable_static_fft_max_lvl_log = fft_max_lvl + self.variable_static_actual_sample_rate = variable_static_actual_sample_rate = actual_sample_rate + self.variable_static_actual_dsp_freq = variable_static_actual_dsp_freq = tune_result.actual_dsp_freq + self.variable_any_code_waterfall_ave = variable_any_code_waterfall_ave = fft_averaging + self.variable_any_code_iq_correction = variable_any_code_iq_correction = None + self.variable_any_code_fft_sink_peak_hold = variable_any_code_fft_sink_peak_hold = fft_peak_hold + self.variable_any_code_fft_sink_ave = variable_any_code_fft_sink_ave = fft_averaging + self.variable_any_code_auto_dc_offset_removal = variable_any_code_auto_dc_offset_removal = None + self.update_time_source = update_time_source = None + self.update_clock_source = update_clock_source = None + self.tune_obj = tune_obj = [requested_freq, uhd.tune_request(requested_freq, lo_offset_txt), uhd.tune_request(requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][tune_mode] + self.static_locked = static_locked = '"' + str(locked_probe) + '"' + self.show_stream_tags_chk = show_stream_tags_chk = [False, True][show_stream_tags.lower() != 'false'] + self.show_max_lvl = show_max_lvl = True + self.show_max_freq = show_max_freq = True + self.selected_gain_proxy = selected_gain_proxy = selected_gain + self.selected_antenna_proxy = selected_antenna_proxy = selected_antenna + self.scope_mode = scope_mode = 0+1 + self.requested_sample_rate = requested_sample_rate = requested_sample_rate_base / (1.*decim) + self.motherboard_sensor_names = motherboard_sensor_names = [] + self.max_decim = max_decim = 256 + self.initial_gain = initial_gain = 0 + self.has_lo_locked = has_lo_locked = False + self.freq_range = freq_range = uhd.freq_range(freq,freq+1) + self.fix_invalid_freq = fix_invalid_freq = None + self.fft_max_lvl_value = fft_max_lvl_value = fft_max_lvl + self.fft_ave_probe = fft_ave_probe = ave + self.daughterboard_sensor_names = daughterboard_sensor_names = [] + self.clicked_freq_txt = clicked_freq_txt = clicked_freq + self.auto_iq_correction = auto_iq_correction = True + self.auto_dc_offset_removal = auto_dc_offset_removal = True + self.any_test_1 = any_test_1 = test + + ################################################## + # Blocks + ################################################## + self.src = uhd.usrp_source( + ",".join((args, "")), + uhd.stream_args( + cpu_format="fc32", + channels=range(1), + ), + + ) + if spec != "": self.src.set_subdev_spec(spec, 0) + self.src.set_samp_rate(requested_sample_rate) + self.src.set_center_freq(uhd.tune_request(freq, lo_offset), 0) + self.src.set_gain(selected_gain_proxy, 0) + self.nb_right = self.nb_right = wx.Notebook(self.GetWin(), style=wx.NB_TOP) + self.nb_right.AddPage(grc_wxgui.Panel(self.nb_right), "Params") + self.GridAdd(self.nb_right, 0, 1, 1, 1) + self.nb = self.nb = wx.Notebook(self.GetWin(), style=wx.NB_TOP) + self.nb.AddPage(grc_wxgui.Panel(self.nb), "FFT") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Scope") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Waterfall") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Sensors") + self.GridAdd(self.nb, 0, 0, 1, 1) + self.nb_test = self.nb_test = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_test.AddPage(grc_wxgui.Panel(self.nb_test), "Test") + self.nb_right.GetPage(0).Add(self.nb_test) + self.nb_analog = self.nb_analog = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_analog.AddPage(grc_wxgui.Panel(self.nb_analog), "Analog") + self.nb_analog.AddPage(grc_wxgui.Panel(self.nb_analog), "DC") + self.nb_right.GetPage(0).Add(self.nb_analog) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_gain_range(): + self._post_any_code_evaluators += [('gain_range', lambda: self._evalutate_gain_range(**{}))] + def __evalutate_gain_range(*args, **kwds): + try: + self.gain_range = self.src.get_gain_range() + self.set_gain_range(self.gain_range) + except AttributeError, e: + print "AttributeError while evaulating gain_range:", e + __post_evalutate_gain_range() + except Exception, e: + print "Exception while evaluating gain_range:", e + self._evalutate_gain_range = __evalutate_gain_range + self.__post_evalutate_gain_range = __post_evalutate_gain_range + self._evalutate_gain_range(**{}) + gain_range = self.gain_range + self.fft_sink = fftsink2.fft_sink_c( + self.nb.GetPage(0).GetWin(), + baseband_freq=fft_center_freq, + y_per_div=10, + y_divs=int(dyn_rng/10), + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=1024*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="FFT Plot", + peak_hold=False, fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(0).Add(self.fft_sink.win) + def fft_sink_callback(x, y): + self.set_clicked_freq(x) + + self.fft_sink.set_callback(fft_sink_callback) + self.waterfall_sink = waterfallsink2.waterfall_sink_c( + self.nb.GetPage(2).GetWin(), + baseband_freq=fft_center_freq, + dynamic_range=dyn_rng, + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=512*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="Waterfall Plot", + fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(2).Add(self.waterfall_sink.win) + def waterfall_sink_callback(x, y): + self.set_clicked_freq(x) + + self.waterfall_sink.set_callback(waterfall_sink_callback) + self._test_chooser = forms.button( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.test, + callback=self.set_test, + label="Test", + choices=[1], + labels=['Reset FFT average'], + ) + self.nb_test.GetPage(0).Add(self._test_chooser) + self._show_stream_tags_chk_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_stream_tags_chk, + callback=self.set_show_stream_tags_chk, + label="Print stream tags", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_stream_tags_chk_check_box, 0, 0, 1, 1) + self._scope_mode_chooser = forms.radio_buttons( + parent=self.nb.GetPage(1).GetWin(), + value=self.scope_mode, + callback=self.set_scope_mode, + label="Scope Mode", + choices=[0, 1], + labels=['Complex', 'Magnitude'], + style=wx.RA_HORIZONTAL, + ) + self.nb.GetPage(1).Add(self._scope_mode_chooser) + self.probe_avg_mag = analog.probe_avg_mag_sqrd_c(0, mag_alpha) + self.nb_rate = self.nb_rate = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_rate.AddPage(grc_wxgui.Panel(self.nb_rate), "Rate") + self.nb_right.GetPage(0).Add(self.nb_rate) + self.nb_info = self.nb_info = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_info.AddPage(grc_wxgui.Panel(self.nb_info), "Info") + self.nb_right.GetPage(0).GridAdd(self.nb_info, 0, 0, 1, 1) + self.nb_freq = self.nb_freq = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_freq.AddPage(grc_wxgui.Panel(self.nb_freq), "Freq") + self.nb_right.GetPage(0).Add(self.nb_freq) + if sensor_interval > 0: + self._motherboard_sensor_names_poll_rate = sensor_interval + else: + self._motherboard_sensor_names_poll_rate = 1 + self._motherboard_sensor_names_enabled = sensor_interval > 0 + def _set_motherboard_sensor_names_poll_rate(rate): + self._motherboard_sensor_names_enabled = rate > 0 + if rate > 0: + self._motherboard_sensor_names_poll_rate = rate + self.set_motherboard_sensor_names_poll_rate = _set_motherboard_sensor_names_poll_rate + def _motherboard_sensor_names_probe(): + while True: + if self._motherboard_sensor_names_enabled: + val = self.src.get_mboard_sensor_names() + try: + self.set_motherboard_sensor_names(val) + except AttributeError: + pass + time.sleep(1.0/(self._motherboard_sensor_names_poll_rate)) + _motherboard_sensor_names_thread = threading.Thread(target=_motherboard_sensor_names_probe) + _motherboard_sensor_names_thread.daemon = True + _motherboard_sensor_names_thread.start() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_initial_gain(): + self._post_any_code_evaluators += [('initial_gain', lambda: self._evalutate_initial_gain(**{'gain_range': gain_range}))] + def __evalutate_initial_gain(*args, **kwds): + try: + self.initial_gain = [gain,gain_range.start() + ((gain_range.stop() - gain_range.start()) * 0.25)][gain==float('-inf')] + self.set_initial_gain(self.initial_gain) + except AttributeError, e: + print "AttributeError while evaulating initial_gain:", e + __post_evalutate_initial_gain() + except Exception, e: + print "Exception while evaluating initial_gain:", e + self._evalutate_initial_gain = __evalutate_initial_gain + self.__post_evalutate_initial_gain = __post_evalutate_initial_gain + self._evalutate_initial_gain(**{'gain_range': gain_range}) + initial_gain = self.initial_gain + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_has_lo_locked(): + self._post_any_code_evaluators += [('has_lo_locked', lambda: self._evalutate_has_lo_locked(**{}))] + def __evalutate_has_lo_locked(*args, **kwds): + try: + self.has_lo_locked = 'lo_locked' in self.src.get_sensor_names() + self.set_has_lo_locked(self.has_lo_locked) + except AttributeError, e: + print "AttributeError while evaulating has_lo_locked:", e + __post_evalutate_has_lo_locked() + except Exception, e: + print "Exception while evaluating has_lo_locked:", e + self._evalutate_has_lo_locked = __evalutate_has_lo_locked + self.__post_evalutate_has_lo_locked = __post_evalutate_has_lo_locked + self._evalutate_has_lo_locked(**{}) + has_lo_locked = self.has_lo_locked + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_freq_range(): + self._post_any_code_evaluators += [('freq_range', lambda: self._evalutate_freq_range(**{}))] + def __evalutate_freq_range(*args, **kwds): + try: + self.freq_range = self.src.get_freq_range() + self.set_freq_range(self.freq_range) + except AttributeError, e: + print "AttributeError while evaulating freq_range:", e + __post_evalutate_freq_range() + except Exception, e: + print "Exception while evaluating freq_range:", e + self._evalutate_freq_range = __evalutate_freq_range + self.__post_evalutate_freq_range = __post_evalutate_freq_range + self._evalutate_freq_range(**{}) + freq_range = self.freq_range + if 5 > 0: + self._fft_ave_probe_poll_rate = 5 + else: + self._fft_ave_probe_poll_rate = 1 + self._fft_ave_probe_enabled = 5 > 0 + def _set_fft_ave_probe_poll_rate(rate): + self._fft_ave_probe_enabled = rate > 0 + if rate > 0: + self._fft_ave_probe_poll_rate = rate + self.set_fft_ave_probe_poll_rate = _set_fft_ave_probe_poll_rate + def _fft_ave_probe_probe(): + while True: + if self._fft_ave_probe_enabled: + val = self.fft_sink.win['avg_alpha'] * [1.0/self.fft_sink.win['avg_alpha'],1.0][self.fft_sink.win['average']];() + try: + self.set_fft_ave_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_ave_probe_poll_rate)) + _fft_ave_probe_thread = threading.Thread(target=_fft_ave_probe_probe) + _fft_ave_probe_thread.daemon = True + _fft_ave_probe_thread.start() + if sensor_interval > 0: + self._daughterboard_sensor_names_poll_rate = sensor_interval + else: + self._daughterboard_sensor_names_poll_rate = 1 + self._daughterboard_sensor_names_enabled = sensor_interval > 0 + def _set_daughterboard_sensor_names_poll_rate(rate): + self._daughterboard_sensor_names_enabled = rate > 0 + if rate > 0: + self._daughterboard_sensor_names_poll_rate = rate + self.set_daughterboard_sensor_names_poll_rate = _set_daughterboard_sensor_names_poll_rate + def _daughterboard_sensor_names_probe(): + while True: + if self._daughterboard_sensor_names_enabled: + val = self.src.get_sensor_names() + try: + self.set_daughterboard_sensor_names(val) + except AttributeError: + pass + time.sleep(1.0/(self._daughterboard_sensor_names_poll_rate)) + _daughterboard_sensor_names_thread = threading.Thread(target=_daughterboard_sensor_names_probe) + _daughterboard_sensor_names_thread.daemon = True + _daughterboard_sensor_names_thread.start() + self._auto_iq_correction_check_box = forms.check_box( + parent=self.nb_analog.GetPage(1).GetWin(), + value=self.auto_iq_correction, + callback=self.set_auto_iq_correction, + label="Auto IQ correction", + true=True, + false=False, + ) + self.nb_analog.GetPage(1).Add(self._auto_iq_correction_check_box) + self._auto_dc_offset_removal_check_box = forms.check_box( + parent=self.nb_analog.GetPage(1).GetWin(), + value=self.auto_dc_offset_removal, + callback=self.set_auto_dc_offset_removal, + label="Auto DC offset removal", + true=True, + false=False, + ) + self.nb_analog.GetPage(1).Add(self._auto_dc_offset_removal_check_box) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_antennas(): + self._post_any_code_evaluators += [('antennas', lambda: self._evalutate_antennas(**{}))] + def __evalutate_antennas(*args, **kwds): + try: + self.antennas = self.src.get_antennas() + self.set_antennas(self.antennas) + except AttributeError, e: + print "AttributeError while evaulating antennas:", e + __post_evalutate_antennas() + except Exception, e: + print "Exception while evaluating antennas:", e + self._evalutate_antennas = __evalutate_antennas + self.__post_evalutate_antennas = __post_evalutate_antennas + self._evalutate_antennas(**{}) + antennas = self.antennas + self.wxgui_scopesink2_0 = scopesink2.scope_sink_c( + self.nb.GetPage(1).GetWin(), + title="Scope Plot", + sample_rate=samp_rate, + v_scale=0, + v_offset=0, + t_scale=0, + ac_couple=False, + xy_mode=False, + num_inputs=1, + trig_mode=wxgui.TRIG_MODE_AUTO, + y_axis_label="Counts", + ) + self.nb.GetPage(1).Add(self.wxgui_scopesink2_0.win) + self._variable_static_usrp_info_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_usrp_info, + callback=self.set_variable_static_usrp_info, + label="USRP", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).GridAdd(self._variable_static_usrp_info_static_text, 0, 0, 1, 10) + self._variable_static_time_now_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_time_now, + callback=self.set_variable_static_time_now, + label="Time now", + converter=forms.str_converter(), + ) + self.nb_test.GetPage(0).GridAdd(self._variable_static_time_now_static_text, 1, 1, 1, 2) + self._variable_static_text_0_0_static_text = forms.static_text( + parent=self.nb.GetPage(3).GetWin(), + value=self.variable_static_text_0_0, + callback=self.set_variable_static_text_0_0, + label="Daughterboard", + converter=forms.str_converter(), + ) + self.nb.GetPage(3).GridAdd(self._variable_static_text_0_0_static_text, 2, 0, 1, 1) + self._variable_static_text_0_static_text = forms.static_text( + parent=self.nb.GetPage(3).GetWin(), + value=self.variable_static_text_0, + callback=self.set_variable_static_text_0, + label="Motherboard", + converter=forms.str_converter(), + ) + self.nb.GetPage(3).GridAdd(self._variable_static_text_0_static_text, 0, 0, 1, 1) + self._variable_static_subdev_spec_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_subdev_spec, + callback=self.set_variable_static_subdev_spec, + label="Sub-device", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).GridAdd(self._variable_static_subdev_spec_static_text, 1, 0, 1, 1) + self._variable_static_rf_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_rf_freq, + callback=self.set_variable_static_rf_freq, + label="Actual RF freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_rf_freq_static_text, 5, 0, 1, 1) + self._variable_static_requested_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_requested_freq, + callback=self.set_variable_static_requested_freq, + label="Requested base + fine freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_requested_freq_static_text, 3, 0, 1, 1) + self._variable_static_max_bin_freq_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_max_bin_freq, + callback=self.set_variable_static_max_bin_freq, + label="Peak freq", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_max_bin_freq_static_text) + self._variable_static_level_probe_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_level_probe, + callback=self.set_variable_static_level_probe, + label="Signal mag^2 (dB)", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_level_probe_static_text) + self._variable_static_fft_window_name_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_fft_window_name, + callback=self.set_variable_static_fft_window_name, + label="FFT window", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).Add(self._variable_static_fft_window_name_static_text) + self._variable_static_fft_max_lvl_log_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_fft_max_lvl_log, + callback=self.set_variable_static_fft_max_lvl_log, + label="Peak magnitude (dB)", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_fft_max_lvl_log_static_text) + self._variable_static_actual_sample_rate_static_text = forms.static_text( + parent=self.nb_rate.GetPage(0).GetWin(), + value=self.variable_static_actual_sample_rate, + callback=self.set_variable_static_actual_sample_rate, + label="Actual", + converter=forms.float_converter(), + ) + self.nb_rate.GetPage(0).GridAdd(self._variable_static_actual_sample_rate_static_text, 0, 1, 1, 1) + self._variable_static_actual_dsp_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_actual_dsp_freq, + callback=self.set_variable_static_actual_dsp_freq, + label="Actual DSP freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_actual_dsp_freq_static_text, 6, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_waterfall_ave(): + self._post_any_code_evaluators += [('variable_any_code_waterfall_ave', lambda: self._evalutate_variable_any_code_waterfall_ave(**{'averaging': fft_averaging}))] + def __evalutate_variable_any_code_waterfall_ave(*args, **kwds): + try: + self.variable_any_code_waterfall_ave = self.waterfall_sink.win['average'] = fft_averaging + self.set_variable_any_code_waterfall_ave(self.variable_any_code_waterfall_ave) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_waterfall_ave:", e + __post_evalutate_variable_any_code_waterfall_ave() + except Exception, e: + print "Exception while evaluating variable_any_code_waterfall_ave:", e + self._evalutate_variable_any_code_waterfall_ave = __evalutate_variable_any_code_waterfall_ave + self.__post_evalutate_variable_any_code_waterfall_ave = __post_evalutate_variable_any_code_waterfall_ave + self.__post_evalutate_variable_any_code_waterfall_ave() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_iq_correction(): + self._post_any_code_evaluators += [('variable_any_code_iq_correction', lambda: self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': auto_iq_correction}))] + def __evalutate_variable_any_code_iq_correction(*args, **kwds): + try: + [self.src.set_auto_iq_balance(self.auto_iq_correction), self.auto_iq_correction or self.src.set_iq_balance(0+0j)] + self.set_variable_any_code_iq_correction(self.variable_any_code_iq_correction) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_iq_correction:", e + __post_evalutate_variable_any_code_iq_correction() + except Exception, e: + print "Exception while evaluating variable_any_code_iq_correction:", e + self._evalutate_variable_any_code_iq_correction = __evalutate_variable_any_code_iq_correction + self.__post_evalutate_variable_any_code_iq_correction = __post_evalutate_variable_any_code_iq_correction + self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': auto_iq_correction}) + variable_any_code_iq_correction = self.variable_any_code_iq_correction + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_fft_sink_peak_hold(): + self._post_any_code_evaluators += [('variable_any_code_fft_sink_peak_hold', lambda: self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': fft_peak_hold}))] + def __evalutate_variable_any_code_fft_sink_peak_hold(*args, **kwds): + try: + self.variable_any_code_fft_sink_peak_hold = self.fft_sink.win['peak_hold'] = fft_peak_hold + self.set_variable_any_code_fft_sink_peak_hold(self.variable_any_code_fft_sink_peak_hold) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_fft_sink_peak_hold:", e + __post_evalutate_variable_any_code_fft_sink_peak_hold() + except Exception, e: + print "Exception while evaluating variable_any_code_fft_sink_peak_hold:", e + self._evalutate_variable_any_code_fft_sink_peak_hold = __evalutate_variable_any_code_fft_sink_peak_hold + self.__post_evalutate_variable_any_code_fft_sink_peak_hold = __post_evalutate_variable_any_code_fft_sink_peak_hold + self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': fft_peak_hold}) + variable_any_code_fft_sink_peak_hold = self.variable_any_code_fft_sink_peak_hold + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_fft_sink_ave(): + self._post_any_code_evaluators += [('variable_any_code_fft_sink_ave', lambda: self._evalutate_variable_any_code_fft_sink_ave(**{'averaging': fft_averaging}))] + def __evalutate_variable_any_code_fft_sink_ave(*args, **kwds): + try: + self.variable_any_code_fft_sink_ave = self.fft_sink.win['average'] = fft_averaging + self.set_variable_any_code_fft_sink_ave(self.variable_any_code_fft_sink_ave) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_fft_sink_ave:", e + __post_evalutate_variable_any_code_fft_sink_ave() + except Exception, e: + print "Exception while evaluating variable_any_code_fft_sink_ave:", e + self._evalutate_variable_any_code_fft_sink_ave = __evalutate_variable_any_code_fft_sink_ave + self.__post_evalutate_variable_any_code_fft_sink_ave = __post_evalutate_variable_any_code_fft_sink_ave + self.__post_evalutate_variable_any_code_fft_sink_ave() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_auto_dc_offset_removal(): + self._post_any_code_evaluators += [('variable_any_code_auto_dc_offset_removal', lambda: self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': auto_dc_offset_removal}))] + def __evalutate_variable_any_code_auto_dc_offset_removal(*args, **kwds): + try: + [self.src.set_auto_dc_offset(self.auto_dc_offset_removal), self.auto_dc_offset_removal or self.src.set_dc_offset(0+0j)] + self.set_variable_any_code_auto_dc_offset_removal(self.variable_any_code_auto_dc_offset_removal) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_auto_dc_offset_removal:", e + __post_evalutate_variable_any_code_auto_dc_offset_removal() + except Exception, e: + print "Exception while evaluating variable_any_code_auto_dc_offset_removal:", e + self._evalutate_variable_any_code_auto_dc_offset_removal = __evalutate_variable_any_code_auto_dc_offset_removal + self.__post_evalutate_variable_any_code_auto_dc_offset_removal = __post_evalutate_variable_any_code_auto_dc_offset_removal + self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': auto_dc_offset_removal}) + variable_any_code_auto_dc_offset_removal = self.variable_any_code_auto_dc_offset_removal + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_usrp_info(): + self._post_any_code_evaluators += [('usrp_info', lambda: self._evalutate_usrp_info(**{}))] + def __evalutate_usrp_info(*args, **kwds): + try: + self.usrp_info = '%s (\'%s\'), %s' % (self.src.get_usrp_info().get('mboard_id'), self.src.get_usrp_info().get('mboard_name'), self.src.get_usrp_info().get('rx_subdev_name')) + self.set_usrp_info(self.usrp_info) + except AttributeError, e: + print "AttributeError while evaulating usrp_info:", e + __post_evalutate_usrp_info() + except Exception, e: + print "Exception while evaluating usrp_info:", e + self._evalutate_usrp_info = __evalutate_usrp_info + self.__post_evalutate_usrp_info = __post_evalutate_usrp_info + self._evalutate_usrp_info(**{}) + usrp_info = self.usrp_info + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_update_time_source(): + self._post_any_code_evaluators += [('update_time_source', lambda: self._evalutate_update_time_source(**{'pps': pps}))] + def __evalutate_update_time_source(*args, **kwds): + try: + if self.pps != '': self.src.set_time_source(self.pps, 0); print '1 PPS =', self.pps; + self.set_update_time_source(self.update_time_source) + except AttributeError, e: + print "AttributeError while evaulating update_time_source:", e + __post_evalutate_update_time_source() + except Exception, e: + print "Exception while evaluating update_time_source:", e + self._evalutate_update_time_source = __evalutate_update_time_source + self.__post_evalutate_update_time_source = __post_evalutate_update_time_source + self._evalutate_update_time_source(**{'pps': pps}) + update_time_source = self.update_time_source + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_update_clock_source(): + self._post_any_code_evaluators += [('update_clock_source', lambda: self._evalutate_update_clock_source(**{'ref': ref}))] + def __evalutate_update_clock_source(*args, **kwds): + try: + if self.ref != '': self.src.set_clock_source(self.ref, 0); print 'Ref =', self.ref; + self.set_update_clock_source(self.update_clock_source) + except AttributeError, e: + print "AttributeError while evaulating update_clock_source:", e + __post_evalutate_update_clock_source() + except Exception, e: + print "Exception while evaluating update_clock_source:", e + self._evalutate_update_clock_source = __evalutate_update_clock_source + self.__post_evalutate_update_clock_source = __post_evalutate_update_clock_source + self._evalutate_update_clock_source(**{'ref': ref}) + update_clock_source = self.update_clock_source + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_tune_result(): + self._post_any_code_evaluators += [('tune_result', lambda: self._evalutate_tune_result(**{'tune_obj': tune_obj}))] + def __evalutate_tune_result(*args, **kwds): + try: + self.tune_result = self.src.set_center_freq(self.tune_obj) + self.set_tune_result(self.tune_result) + except AttributeError, e: + print "AttributeError while evaulating tune_result:", e + __post_evalutate_tune_result() + except Exception, e: + print "Exception while evaluating tune_result:", e + self._evalutate_tune_result = __evalutate_tune_result + self.__post_evalutate_tune_result = __post_evalutate_tune_result + self._evalutate_tune_result(**{'tune_obj': tune_obj}) + tune_result = self.tune_result + self._tune_mode_chooser = forms.drop_down( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.tune_mode, + callback=self.set_tune_mode, + label="Tune mode", + choices=[0, 1, 2], + labels=['Auto (no LO offset)', 'Auto with LO offset', 'Manual (no DSP)'], + ) + self.nb_freq.GetPage(0).GridAdd(self._tune_mode_chooser, 0, 0, 1, 1) + if sensor_interval > 0: + self._time_probe_poll_rate = sensor_interval + else: + self._time_probe_poll_rate = 1 + self._time_probe_enabled = sensor_interval > 0 + def _set_time_probe_poll_rate(rate): + self._time_probe_enabled = rate > 0 + if rate > 0: + self._time_probe_poll_rate = rate + self.set_time_probe_poll_rate = _set_time_probe_poll_rate + def _time_probe_probe(): + while True: + if self._time_probe_enabled: + val = self.src.get_time_now() + try: + self.set_time_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._time_probe_poll_rate)) + _time_probe_thread = threading.Thread(target=_time_probe_probe) + _time_probe_thread.daemon = True + _time_probe_thread.start() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_subdev_spec(): + self._post_any_code_evaluators += [('subdev_spec', lambda: self._evalutate_subdev_spec(**{}))] + def __evalutate_subdev_spec(*args, **kwds): + try: + self.subdev_spec = '[' + self.src.get_subdev_spec().strip() + ']' + self.set_subdev_spec(self.subdev_spec) + except AttributeError, e: + print "AttributeError while evaulating subdev_spec:", e + __post_evalutate_subdev_spec() + except Exception, e: + print "Exception while evaluating subdev_spec:", e + self._evalutate_subdev_spec = __evalutate_subdev_spec + self.__post_evalutate_subdev_spec = __post_evalutate_subdev_spec + self._evalutate_subdev_spec(**{}) + subdev_spec = self.subdev_spec + self._static_locked_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.static_locked, + callback=self.set_static_locked, + label="Locked", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).Add(self._static_locked_static_text) + if probe_interval > 0: + self._signal_probe_poll_rate = probe_interval + else: + self._signal_probe_poll_rate = 1 + self._signal_probe_enabled = probe_interval > 0 + def _set_signal_probe_poll_rate(rate): + self._signal_probe_enabled = rate > 0 + if rate > 0: + self._signal_probe_poll_rate = rate + self.set_signal_probe_poll_rate = _set_signal_probe_poll_rate + def _signal_probe_probe(): + while True: + if self._signal_probe_enabled: + val = self.probe_avg_mag.level() + try: + self.set_signal_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._signal_probe_poll_rate)) + _signal_probe_thread = threading.Thread(target=_signal_probe_probe) + _signal_probe_thread.daemon = True + _signal_probe_thread.start() + self._show_max_lvl_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_max_lvl, + callback=self.set_show_max_lvl, + label="Show max level", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_max_lvl_check_box, 0, 2, 1, 1) + self._show_max_freq_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_max_freq, + callback=self.set_show_max_freq, + label="Show max freq", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_max_freq_check_box, 0, 1, 1, 1) + _selected_gain_sizer = wx.BoxSizer(wx.VERTICAL) + self._selected_gain_text_box = forms.text_box( + parent=self.nb_analog.GetPage(0).GetWin(), + sizer=_selected_gain_sizer, + value=self.selected_gain, + callback=self.set_selected_gain, + label="Gain", + converter=forms.float_converter(), + proportion=0, + ) + self._selected_gain_slider = forms.slider( + parent=self.nb_analog.GetPage(0).GetWin(), + sizer=_selected_gain_sizer, + value=self.selected_gain, + callback=self.set_selected_gain, + minimum=gain_range.start(), + maximum=[gain_range.stop(), gain_range.start() + 1.0][gain_range.stop()==gain_range.start()], + num_steps=[int((abs(gain_range.stop()-gain_range.start())/[gain_range.step(), 1.0][gain_range.step()==0])), 1][gain_range.stop()==gain_range.start()], + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_analog.GetPage(0).Add(_selected_gain_sizer) + self._selected_antenna_chooser = forms.radio_buttons( + parent=self.nb_analog.GetPage(0).GetWin(), + value=self.selected_antenna, + callback=self.set_selected_antenna, + label="Antenna", + choices=antennas, + labels=[antennas,['(default)']][antennas==('',)], + style=wx.RA_HORIZONTAL, + ) + self.nb_analog.GetPage(0).Add(self._selected_antenna_chooser) + self._requested_sample_rate_base_text_box = forms.text_box( + parent=self.nb_rate.GetPage(0).GetWin(), + value=self.requested_sample_rate_base, + callback=self.set_requested_sample_rate_base, + label="Requested base sample rate", + converter=forms.float_converter(), + ) + self.nb_rate.GetPage(0).GridAdd(self._requested_sample_rate_base_text_box, 0, 0, 1, 1) + _requested_freq_txt_sizer = wx.BoxSizer(wx.VERTICAL) + self._requested_freq_txt_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_requested_freq_txt_sizer, + value=self.requested_freq_txt, + callback=self.set_requested_freq_txt, + label="Requested freq", + converter=forms.float_converter(), + proportion=0, + ) + self._requested_freq_txt_slider = forms.slider( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_requested_freq_txt_sizer, + value=self.requested_freq_txt, + callback=self.set_requested_freq_txt, + minimum=freq_range.start(), + maximum=[freq_range.stop(), freq_range.start() + 1.0][freq_range.start()==freq_range.stop()], + num_steps=[1000,1][freq_range.start()==freq_range.stop()], + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_freq.GetPage(0).GridAdd(_requested_freq_txt_sizer, 1, 0, 1, 1) + self._relative_time_chooser = forms.drop_down( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.relative_time, + callback=self.set_relative_time, + label="Time display", + choices=[False, True], + labels=['Absolute', 'Relative'], + ) + self.nb_test.GetPage(0).GridAdd(self._relative_time_chooser, 1, 0, 1, 1) + self._relative_freq_chooser = forms.drop_down( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.relative_freq, + callback=self.set_relative_freq, + label="Frequency Axis", + choices=[1, 0], + labels=['RF', 'Baseband'], + ) + self.nb_freq.GetPage(0).GridAdd(self._relative_freq_chooser, 7, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_motherboard_sensors(): + self._post_any_code_evaluators += [('motherboard_sensors', lambda: self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': motherboard_sensor_names}))] + def __evalutate_motherboard_sensors(*args, **kwds): + try: + self.motherboard_sensors = '\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_mboard_sensor(x))), filter(lambda x: x.find('gps') != 0, self.motherboard_sensor_names))) + self.set_motherboard_sensors(self.motherboard_sensors) + except AttributeError, e: + print "AttributeError while evaulating motherboard_sensors:", e + __post_evalutate_motherboard_sensors() + except Exception, e: + print "Exception while evaluating motherboard_sensors:", e + self._evalutate_motherboard_sensors = __evalutate_motherboard_sensors + self.__post_evalutate_motherboard_sensors = __post_evalutate_motherboard_sensors + self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': motherboard_sensor_names}) + motherboard_sensors = self.motherboard_sensors + self.logpwrfft_x_0 = logpwrfft.logpwrfft_c( + sample_rate=samp_rate, + fft_size=fft_size, + ref_scale=fft_ref_scale, + frame_rate=fft_rate, + avg_alpha=fft_ave_probe, + average=False, + ) + if lo_check_interval * float(has_lo_locked) > 0: + self._locked_probe_poll_rate = lo_check_interval * float(has_lo_locked) + else: + self._locked_probe_poll_rate = 1 + self._locked_probe_enabled = lo_check_interval * float(has_lo_locked) > 0 + def _set_locked_probe_poll_rate(rate): + self._locked_probe_enabled = rate > 0 + if rate > 0: + self._locked_probe_poll_rate = rate + self.set_locked_probe_poll_rate = _set_locked_probe_poll_rate + def _locked_probe_probe(): + while True: + if self._locked_probe_enabled: + val = self.src.get_sensor('lo_locked') + try: + self.set_locked_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._locked_probe_poll_rate)) + _locked_probe_thread = threading.Thread(target=_locked_probe_probe) + _locked_probe_thread.daemon = True + _locked_probe_thread.start() + self._lo_offset_txt_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.lo_offset_txt, + callback=self.set_lo_offset_txt, + label="LO offset (only for LO offset tuning mode)", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._lo_offset_txt_text_box, 4, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_gain_default(): + self._post_any_code_evaluators += [('gain_default', lambda: self._evalutate_gain_default(**{'initial_gain': initial_gain}))] + def __evalutate_gain_default(*args, **kwds): + try: + self.gain_default = [gain,initial_gain][gain==float('-inf')] + self.set_gain_default(self.gain_default) + except AttributeError, e: + print "AttributeError while evaulating gain_default:", e + __post_evalutate_gain_default() + except Exception, e: + print "Exception while evaluating gain_default:", e + self._evalutate_gain_default = __evalutate_gain_default + self.__post_evalutate_gain_default = __post_evalutate_gain_default + self._evalutate_gain_default(**{'initial_gain': initial_gain}) + gain_default = self.gain_default + _freq_fine_sizer = wx.BoxSizer(wx.VERTICAL) + self._freq_fine_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_freq_fine_sizer, + value=self.freq_fine, + callback=self.set_freq_fine, + label="Freq (fine)", + converter=forms.float_converter(), + proportion=0, + ) + self._freq_fine_slider = forms.slider( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_freq_fine_sizer, + value=self.freq_fine, + callback=self.set_freq_fine, + minimum=-freq_fine_range/2, + maximum=freq_fine_range/2, + num_steps=1000, + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_freq.GetPage(0).GridAdd(_freq_fine_sizer, 2, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_fix_invalid_freq(): + self._post_any_code_evaluators += [('fix_invalid_freq', lambda: self._evalutate_fix_invalid_freq(**{}))] + def __evalutate_fix_invalid_freq(*args, **kwds): + try: + ((freq <= freq_range.stop()) and (freq >= freq_range.start())) or self.set_requested_freq_txt(self.src.get_center_freq()); print self.src.get_center_freq() + self.set_fix_invalid_freq(self.fix_invalid_freq) + except AttributeError, e: + print "AttributeError while evaulating fix_invalid_freq:", e + __post_evalutate_fix_invalid_freq() + except Exception, e: + print "Exception while evaluating fix_invalid_freq:", e + self._evalutate_fix_invalid_freq = __evalutate_fix_invalid_freq + self.__post_evalutate_fix_invalid_freq = __post_evalutate_fix_invalid_freq + self.__post_evalutate_fix_invalid_freq() + self.fft_max_lvl_probe = blocks.probe_signal_f() + if probe_interval > 0: + self._fft_max_lvl_poll_rate = probe_interval + else: + self._fft_max_lvl_poll_rate = 1 + self._fft_max_lvl_enabled = probe_interval > 0 + def _set_fft_max_lvl_poll_rate(rate): + self._fft_max_lvl_enabled = rate > 0 + if rate > 0: + self._fft_max_lvl_poll_rate = rate + self.set_fft_max_lvl_poll_rate = _set_fft_max_lvl_poll_rate + def _fft_max_lvl_probe(): + while True: + if self._fft_max_lvl_enabled: + val = self.fft_max_lvl_probe.level() + try: + self.set_fft_max_lvl(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_max_lvl_poll_rate)) + _fft_max_lvl_thread = threading.Thread(target=_fft_max_lvl_probe) + _fft_max_lvl_thread.daemon = True + _fft_max_lvl_thread.start() + self.fft_max_idx_probe = blocks.probe_signal_s() + if probe_interval > 0: + self._fft_max_idx_poll_rate = probe_interval + else: + self._fft_max_idx_poll_rate = 1 + self._fft_max_idx_enabled = probe_interval > 0 + def _set_fft_max_idx_poll_rate(rate): + self._fft_max_idx_enabled = rate > 0 + if rate > 0: + self._fft_max_idx_poll_rate = rate + self.set_fft_max_idx_poll_rate = _set_fft_max_idx_poll_rate + def _fft_max_idx_probe(): + while True: + if self._fft_max_idx_enabled: + val = self.fft_max_idx_probe.level() + try: + self.set_fft_max_idx(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_max_idx_poll_rate)) + _fft_max_idx_thread = threading.Thread(target=_fft_max_idx_probe) + _fft_max_idx_thread.daemon = True + _fft_max_idx_thread.start() + _decim_sizer = wx.BoxSizer(wx.VERTICAL) + self._decim_text_box = forms.text_box( + parent=self.nb_rate.GetPage(0).GetWin(), + sizer=_decim_sizer, + value=self.decim, + callback=self.set_decim, + label="Divide base sample rate", + converter=forms.int_converter(), + proportion=0, + ) + self._decim_slider = forms.slider( + parent=self.nb_rate.GetPage(0).GetWin(), + sizer=_decim_sizer, + value=self.decim, + callback=self.set_decim, + minimum=1, + maximum=max_decim, + num_steps=max_decim-1, + style=wx.SL_HORIZONTAL, + cast=int, + proportion=1, + ) + self.nb_rate.GetPage(0).Add(_decim_sizer) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_daughterboard_sensors(): + self._post_any_code_evaluators += [('daughterboard_sensors', lambda: self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': daughterboard_sensor_names}))] + def __evalutate_daughterboard_sensors(*args, **kwds): + try: + self.daughterboard_sensors = '\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_sensor(x))), self.daughterboard_sensor_names)) + self.set_daughterboard_sensors(self.daughterboard_sensors) + except AttributeError, e: + print "AttributeError while evaulating daughterboard_sensors:", e + __post_evalutate_daughterboard_sensors() + except Exception, e: + print "Exception while evaluating daughterboard_sensors:", e + self._evalutate_daughterboard_sensors = __evalutate_daughterboard_sensors + self.__post_evalutate_daughterboard_sensors = __post_evalutate_daughterboard_sensors + self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': daughterboard_sensor_names}) + daughterboard_sensors = self.daughterboard_sensors + self._clicked_freq_txt_text_box = forms.text_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.clicked_freq_txt, + callback=self.set_clicked_freq_txt, + label="Clicked freq", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._clicked_freq_txt_text_box) + self.blocks_tag_debug_0 = blocks.tag_debug(gr.sizeof_gr_complex*1, "", ""); self.blocks_tag_debug_0.set_display(show_stream_tags_chk) + self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_short*1) + self.blocks_max_xx_0 = blocks.max_ff(fft_size) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.blocks_complex_to_mag_0 = blocks.complex_to_mag(1) + self.blocks_argmax_xx_0 = blocks.argmax_fs(fft_size) + self.blks2_selector_0_0 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=1, + num_outputs=2, + input_index=0, + output_index=scope_mode, + ) + self.blks2_selector_0 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=2, + num_outputs=1, + input_index=scope_mode, + output_index=0, + ) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_any_test_1(): + self._post_any_code_evaluators += [('any_test_1', lambda: self._evalutate_any_test_1(**{'test': test}))] + def __evalutate_any_test_1(*args, **kwds): + try: + if self.fft_sink.controller['average']: self.fft_sink.controller['average'] = False; time.sleep(0.25); self.fft_sink.controller['average'] = True; + self.set_any_test_1(self.any_test_1) + except AttributeError, e: + print "AttributeError while evaulating any_test_1:", e + __post_evalutate_any_test_1() + except Exception, e: + print "Exception while evaluating any_test_1:", e + self._evalutate_any_test_1 = __evalutate_any_test_1 + self.__post_evalutate_any_test_1 = __post_evalutate_any_test_1 + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_actual_sample_rate(): + self._post_any_code_evaluators += [('actual_sample_rate', lambda: self._evalutate_actual_sample_rate(**{'requested_sample_rate': requested_sample_rate}))] + def __evalutate_actual_sample_rate(*args, **kwds): + try: + self.actual_sample_rate = self.src.get_samp_rate() + self.set_actual_sample_rate(self.actual_sample_rate) + except AttributeError, e: + print "AttributeError while evaulating actual_sample_rate:", e + __post_evalutate_actual_sample_rate() + except Exception, e: + print "Exception while evaluating actual_sample_rate:", e + self._evalutate_actual_sample_rate = __evalutate_actual_sample_rate + self.__post_evalutate_actual_sample_rate = __post_evalutate_actual_sample_rate + self._evalutate_actual_sample_rate(**{'requested_sample_rate': requested_sample_rate}) + actual_sample_rate = self.actual_sample_rate + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_actual_center_freq(): + self._post_any_code_evaluators += [('actual_center_freq', lambda: self._evalutate_actual_center_freq(**{'tune_obj': tune_obj}))] + def __evalutate_actual_center_freq(*args, **kwds): + try: + self.actual_center_freq = self.src.get_center_freq() + self.set_actual_center_freq(self.actual_center_freq) + except AttributeError, e: + print "AttributeError while evaulating actual_center_freq:", e + __post_evalutate_actual_center_freq() + except Exception, e: + print "Exception while evaluating actual_center_freq:", e + self._evalutate_actual_center_freq = __evalutate_actual_center_freq + self.__post_evalutate_actual_center_freq = __post_evalutate_actual_center_freq + self._evalutate_actual_center_freq(**{'tune_obj': tune_obj}) + actual_center_freq = self.actual_center_freq + + ################################################## + # Connections + ################################################## + self.connect((self.blks2_selector_0, 0), (self.wxgui_scopesink2_0, 0)) + self.connect((self.blks2_selector_0_0, 0), (self.blks2_selector_0, 0)) + self.connect((self.blks2_selector_0_0, 1), (self.blocks_complex_to_mag_0, 0)) + self.connect((self.blocks_argmax_xx_0, 1), (self.blocks_null_sink_0, 0)) + self.connect((self.blocks_argmax_xx_0, 0), (self.fft_max_idx_probe, 0)) + self.connect((self.blocks_complex_to_mag_0, 0), (self.blocks_float_to_complex_0, 1)) + self.connect((self.blocks_complex_to_mag_0, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_float_to_complex_0, 0), (self.blks2_selector_0, 1)) + self.connect((self.blocks_max_xx_0, 0), (self.fft_max_lvl_probe, 0)) + self.connect((self.logpwrfft_x_0, 0), (self.blocks_argmax_xx_0, 0)) + self.connect((self.logpwrfft_x_0, 0), (self.blocks_max_xx_0, 0)) + self.connect((self.src, 0), (self.blocks_tag_debug_0, 0)) + self.connect((self.src, 0), (self.blks2_selector_0_0, 0)) + self.connect((self.src, 0), (self.fft_sink, 0)) + self.connect((self.src, 0), (self.waterfall_sink, 0)) + self.connect((self.src, 0), (self.probe_avg_mag, 0)) + self.connect((self.src, 0), (self.logpwrfft_x_0, 0)) + + + def get_antenna(self): + return self.antenna + + def set_antenna(self, antenna): + self.antenna = antenna + self.set_selected_antenna([ [self.antenna,self.antennas[0]][self.antenna not in self.antennas] ,self.antennas[0]][self.antenna=='']) + + def get_args(self): + return self.args + + def set_args(self, args): + self.args = args + + def get_ave(self): + return self.ave + + def set_ave(self, ave): + self.ave = ave + self.set_fft_ave_probe(self.ave) + + def get_averaging(self): + return self.averaging + + def set_averaging(self, averaging): + self.averaging = averaging + self.set_fft_averaging(self.str_to_bool(self.averaging)) + + def get_bw(self): + return self.bw + + def set_bw(self, bw): + self.bw = bw + self.src.set_bandwidth(self.bw, 0) + + def get_dyn_rng(self): + return self.dyn_rng + + def set_dyn_rng(self, dyn_rng): + self.dyn_rng = dyn_rng + + def get_fft_rate(self): + return self.fft_rate + + def set_fft_rate(self, fft_rate): + self.fft_rate = fft_rate + + def get_fft_ref_scale(self): + return self.fft_ref_scale + + def set_fft_ref_scale(self, fft_ref_scale): + self.fft_ref_scale = fft_ref_scale + + def get_fft_size(self): + return self.fft_size + + def set_fft_size(self, fft_size): + self.fft_size = fft_size + self.set_fft_max_idx((self.fft_size/2)*0) + self.set_fft_max_idx_norm([self.fft_max_idx,self.fft_max_idx-self.fft_size][int(self.fft_max_idx>(self.fft_size/2))]) + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + + def get_freq(self): + return self.freq + + def set_freq(self, freq): + self.freq = freq + self.set_requested_freq_txt(self.freq) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 0) + + def get_freq_fine_range(self): + return self.freq_fine_range + + def set_freq_fine_range(self, freq_fine_range): + self.freq_fine_range = freq_fine_range + + def get_gain(self): + return self.gain + + def set_gain(self, gain): + self.gain = gain + + def get_lo_check_interval(self): + return self.lo_check_interval + + def set_lo_check_interval(self, lo_check_interval): + self.lo_check_interval = lo_check_interval + self.set_locked_probe_poll_rate(self.lo_check_interval * float(self.has_lo_locked)) + + def get_lo_offset(self): + return self.lo_offset + + def set_lo_offset(self, lo_offset): + self.lo_offset = lo_offset + self.set_lo_offset_txt(self.lo_offset) + self.set_tune_mode([1,0][self.lo_offset==0.0]) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 0) + + def get_mag_alpha(self): + return self.mag_alpha + + def set_mag_alpha(self, mag_alpha): + self.mag_alpha = mag_alpha + self.probe_avg_mag.set_alpha(self.mag_alpha) + + def get_peak_hold(self): + return self.peak_hold + + def set_peak_hold(self, peak_hold): + self.peak_hold = peak_hold + self.set_fft_peak_hold(self.str_to_bool(self.peak_hold)) + + def get_pps(self): + return self.pps + + def set_pps(self, pps): + self.pps = pps + self._evalutate_update_time_source(**{'pps': self.pps}) + + def get_probe_interval(self): + return self.probe_interval + + def set_probe_interval(self, probe_interval): + self.probe_interval = probe_interval + self.set_fft_max_idx_poll_rate(self.probe_interval) + self.set_fft_max_lvl_poll_rate(self.probe_interval) + self.set_signal_probe_poll_rate(self.probe_interval) + + def get_rate(self): + return self.rate + + def set_rate(self, rate): + self.rate = rate + self.set_requested_sample_rate_base(self.rate) + + def get_ref(self): + return self.ref + + def set_ref(self, ref): + self.ref = ref + self._evalutate_update_clock_source(**{'ref': self.ref}) + + def get_ref_lvl(self): + return self.ref_lvl + + def set_ref_lvl(self, ref_lvl): + self.ref_lvl = ref_lvl + + def get_sensor_interval(self): + return self.sensor_interval + + def set_sensor_interval(self, sensor_interval): + self.sensor_interval = sensor_interval + self.set_daughterboard_sensor_names_poll_rate(self.sensor_interval) + self.set_motherboard_sensor_names_poll_rate(self.sensor_interval) + self.set_time_probe_poll_rate(self.sensor_interval) + + def get_show_stream_tags(self): + return self.show_stream_tags + + def set_show_stream_tags(self, show_stream_tags): + self.show_stream_tags = show_stream_tags + self.set_show_stream_tags_chk([False, True][self.show_stream_tags.lower() != 'false']) + + def get_spec(self): + return self.spec + + def set_spec(self, spec): + self.spec = spec + + def get_stream_args(self): + return self.stream_args + + def set_stream_args(self, stream_args): + self.stream_args = stream_args + + def get_window(self): + return self.window + + def set_window(self, window): + self.window = window + self.set_variable_static_fft_window_name(self.window_fn_name_map[self.window]) + self.set_window_fn(self.window_fn_map[self.window]) + + def get_wire_format(self): + return self.wire_format + + def set_wire_format(self, wire_format): + self.wire_format = wire_format + + def get_relative_freq(self): + return self.relative_freq + + def set_relative_freq(self, relative_freq): + self.relative_freq = relative_freq + self.set_fft_center_freq(self.actual_center_freq * self.relative_freq) + self._relative_freq_chooser.set_value(self.relative_freq) + + def get_gain_range(self): + return self.gain_range + + def set_gain_range(self, gain_range): + self.gain_range = gain_range + self._run_evaluators_later(('initial_gain', lambda: self._evalutate_initial_gain(**{'gain_range': self.gain_range}))) + + def get_fft_max_idx(self): + return self.fft_max_idx + + def set_fft_max_idx(self, fft_max_idx): + self.fft_max_idx = fft_max_idx + self.set_fft_max_idx_norm([self.fft_max_idx,self.fft_max_idx-self.fft_size][int(self.fft_max_idx>(self.fft_size/2))]) + + def get_actual_sample_rate(self): + return self.actual_sample_rate + + def set_actual_sample_rate(self, actual_sample_rate): + self.actual_sample_rate = actual_sample_rate + self.set_samp_rate(int(self.actual_sample_rate)) + self.set_variable_static_actual_sample_rate(self.actual_sample_rate) + + def get_actual_center_freq(self): + return self.actual_center_freq + + def set_actual_center_freq(self, actual_center_freq): + self.actual_center_freq = actual_center_freq + self.set_fft_center_freq(self.actual_center_freq * self.relative_freq) + + def get_str_to_bool(self): + return self.str_to_bool + + def set_str_to_bool(self, str_to_bool): + self.str_to_bool = str_to_bool + self.set_fft_averaging(self.str_to_bool(self.averaging)) + self.set_fft_peak_hold(self.str_to_bool(self.peak_hold)) + + def get_signal_probe(self): + return self.signal_probe + + def set_signal_probe(self, signal_probe): + self.signal_probe = signal_probe + self.set_signal_probe_log(math.log10([self.signal_probe,1.0][self.signal_probe==0.0])*10) + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + self.fft_sink.set_sample_rate(self.samp_rate) + self.logpwrfft_x_0.set_sample_rate(self.samp_rate) + self.waterfall_sink.set_sample_rate(self.samp_rate) + self.wxgui_scopesink2_0.set_sample_rate(self.samp_rate) + + def get_requested_freq_txt(self): + return self.requested_freq_txt + + def set_requested_freq_txt(self, requested_freq_txt): + self.requested_freq_txt = requested_freq_txt + self.set_requested_freq(self.requested_freq_txt + self.freq_fine) + self._requested_freq_txt_slider.set_value(self.requested_freq_txt) + self._requested_freq_txt_text_box.set_value(self.requested_freq_txt) + + def get_gain_default(self): + return self.gain_default + + def set_gain_default(self, gain_default): + self.gain_default = gain_default + self.set_selected_gain(self.gain_default) + + def get_freq_fine(self): + return self.freq_fine + + def set_freq_fine(self, freq_fine): + self.freq_fine = freq_fine + self._freq_fine_slider.set_value(self.freq_fine) + self._freq_fine_text_box.set_value(self.freq_fine) + self.set_requested_freq(self.requested_freq_txt + self.freq_fine) + + def get_fft_max_idx_norm(self): + return self.fft_max_idx_norm + + def set_fft_max_idx_norm(self, fft_max_idx_norm): + self.fft_max_idx_norm = fft_max_idx_norm + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + + def get_fft_center_freq(self): + return self.fft_center_freq + + def set_fft_center_freq(self, fft_center_freq): + self.fft_center_freq = fft_center_freq + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + self.fft_sink.set_baseband_freq(self.fft_center_freq) + self.waterfall_sink.set_baseband_freq(self.fft_center_freq) + + def get_antennas(self): + return self.antennas + + def set_antennas(self, antennas): + self.antennas = antennas + self.set_selected_antenna([ [self.antenna,self.antennas[0]][self.antenna not in self.antennas] ,self.antennas[0]][self.antenna=='']) + + def get_window_fn_name_map(self): + return self.window_fn_name_map + + def set_window_fn_name_map(self, window_fn_name_map): + self.window_fn_name_map = window_fn_name_map + self.set_variable_static_fft_window_name(self.window_fn_name_map[self.window]) + + def get_window_fn_map(self): + return self.window_fn_map + + def set_window_fn_map(self, window_fn_map): + self.window_fn_map = window_fn_map + self.set_window_fn(self.window_fn_map[self.window]) + + def get_usrp_info(self): + return self.usrp_info + + def set_usrp_info(self, usrp_info): + self.usrp_info = usrp_info + self.set_variable_static_usrp_info(self.usrp_info) + + def get_tune_result(self): + return self.tune_result + + def set_tune_result(self, tune_result): + self.tune_result = tune_result + self.set_variable_static_actual_dsp_freq(self.tune_result.actual_dsp_freq) + self.set_variable_static_rf_freq(self.tune_result.actual_rf_freq) + + def get_tune_mode(self): + return self.tune_mode + + def set_tune_mode(self, tune_mode): + self.tune_mode = tune_mode + self._tune_mode_chooser.set_value(self.tune_mode) + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + + def get_time_probe(self): + return self.time_probe + + def set_time_probe(self, time_probe): + self.time_probe = time_probe + self.set_variable_static_time_now(str( [time.ctime(self.time_probe.get_real_secs()), datetime.timedelta(seconds=self.time_probe.get_real_secs()), time.gmtime(self.time_probe.get_real_secs())] [self.relative_time])) + + def get_test(self): + return self.test + + def set_test(self, test): + self.test = test + self._run_evaluators_later(('any_test_1', lambda: self._evalutate_any_test_1(**{'test': self.test}))) + self._test_chooser.set_value(self.test) + + def get_subdev_spec(self): + return self.subdev_spec + + def set_subdev_spec(self, subdev_spec): + self.subdev_spec = subdev_spec + self.set_variable_static_subdev_spec(self.subdev_spec) + + def get_signal_probe_log(self): + return self.signal_probe_log + + def set_signal_probe_log(self, signal_probe_log): + self.signal_probe_log = signal_probe_log + self.set_variable_static_level_probe(self.signal_probe_log) + + def get_selected_gain(self): + return self.selected_gain + + def set_selected_gain(self, selected_gain): + self.selected_gain = selected_gain + self._selected_gain_slider.set_value(self.selected_gain) + self._selected_gain_text_box.set_value(self.selected_gain) + self.set_selected_gain_proxy(self.selected_gain) + + def get_selected_antenna(self): + return self.selected_antenna + + def set_selected_antenna(self, selected_antenna): + self.selected_antenna = selected_antenna + self._selected_antenna_chooser.set_value(self.selected_antenna) + self.set_selected_antenna_proxy(self.selected_antenna) + + def get_requested_sample_rate_base(self): + return self.requested_sample_rate_base + + def set_requested_sample_rate_base(self, requested_sample_rate_base): + self.requested_sample_rate_base = requested_sample_rate_base + self.set_requested_sample_rate(self.requested_sample_rate_base / (1.*self.decim)) + self._requested_sample_rate_base_text_box.set_value(self.requested_sample_rate_base) + + def get_requested_freq(self): + return self.requested_freq + + def set_requested_freq(self, requested_freq): + self.requested_freq = requested_freq + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + self.set_variable_static_requested_freq(self.requested_freq) + + def get_relative_time(self): + return self.relative_time + + def set_relative_time(self, relative_time): + self.relative_time = relative_time + self._relative_time_chooser.set_value(self.relative_time) + self.set_variable_static_time_now(str( [time.ctime(self.time_probe.get_real_secs()), datetime.timedelta(seconds=self.time_probe.get_real_secs()), time.gmtime(self.time_probe.get_real_secs())] [self.relative_time])) + + def get_motherboard_sensors(self): + return self.motherboard_sensors + + def set_motherboard_sensors(self, motherboard_sensors): + self.motherboard_sensors = motherboard_sensors + self.set_variable_static_text_0(self.motherboard_sensors) + + def get_max_bin_freq(self): + return self.max_bin_freq + + def set_max_bin_freq(self, max_bin_freq): + self.max_bin_freq = max_bin_freq + self.set_variable_static_max_bin_freq(self.max_bin_freq) + + def get_locked_probe(self): + return self.locked_probe + + def set_locked_probe(self, locked_probe): + self.locked_probe = locked_probe + self.set_static_locked('"' + str(self.locked_probe) + '"') + + def get_lo_offset_txt(self): + return self.lo_offset_txt + + def set_lo_offset_txt(self, lo_offset_txt): + self.lo_offset_txt = lo_offset_txt + self._lo_offset_txt_text_box.set_value(self.lo_offset_txt) + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + + def get_fft_peak_hold(self): + return self.fft_peak_hold + + def set_fft_peak_hold(self, fft_peak_hold): + self.fft_peak_hold = fft_peak_hold + self._run_evaluators_later(('variable_any_code_fft_sink_peak_hold', lambda: self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': self.fft_peak_hold}))) + + def get_fft_max_lvl(self): + return self.fft_max_lvl + + def set_fft_max_lvl(self, fft_max_lvl): + self.fft_max_lvl = fft_max_lvl + self.set_fft_max_lvl_value(self.fft_max_lvl) + self.set_variable_static_fft_max_lvl_log(self.fft_max_lvl) + + def get_fft_averaging(self): + return self.fft_averaging + + def set_fft_averaging(self, fft_averaging): + self.fft_averaging = fft_averaging + self._run_evaluators_later(('variable_any_code_fft_sink_ave', lambda: self._evalutate_variable_any_code_fft_sink_ave(**{'averaging': self.fft_averaging}))) + self._run_evaluators_later(('variable_any_code_waterfall_ave', lambda: self._evalutate_variable_any_code_waterfall_ave(**{'averaging': self.fft_averaging}))) + + def get_decim(self): + return self.decim + + def set_decim(self, decim): + self.decim = decim + self._decim_slider.set_value(self.decim) + self._decim_text_box.set_value(self.decim) + self.set_requested_sample_rate(self.requested_sample_rate_base / (1.*self.decim)) + + def get_daughterboard_sensors(self): + return self.daughterboard_sensors + + def set_daughterboard_sensors(self, daughterboard_sensors): + self.daughterboard_sensors = daughterboard_sensors + self.set_variable_static_text_0_0(self.daughterboard_sensors) + + def get_clicked_freq(self): + return self.clicked_freq + + def set_clicked_freq(self, clicked_freq): + self.clicked_freq = clicked_freq + self.set_clicked_freq_txt(self.clicked_freq) + + def get_window_fn(self): + return self.window_fn + + def set_window_fn(self, window_fn): + self.window_fn = window_fn + + def get_variable_static_usrp_info(self): + return self.variable_static_usrp_info + + def set_variable_static_usrp_info(self, variable_static_usrp_info): + self.variable_static_usrp_info = variable_static_usrp_info + self._variable_static_usrp_info_static_text.set_value(self.variable_static_usrp_info) + + def get_variable_static_time_now(self): + return self.variable_static_time_now + + def set_variable_static_time_now(self, variable_static_time_now): + self.variable_static_time_now = variable_static_time_now + self._variable_static_time_now_static_text.set_value(self.variable_static_time_now) + + def get_variable_static_text_0_0(self): + return self.variable_static_text_0_0 + + def set_variable_static_text_0_0(self, variable_static_text_0_0): + self.variable_static_text_0_0 = variable_static_text_0_0 + self._variable_static_text_0_0_static_text.set_value(self.variable_static_text_0_0) + + def get_variable_static_text_0(self): + return self.variable_static_text_0 + + def set_variable_static_text_0(self, variable_static_text_0): + self.variable_static_text_0 = variable_static_text_0 + self._variable_static_text_0_static_text.set_value(self.variable_static_text_0) + + def get_variable_static_subdev_spec(self): + return self.variable_static_subdev_spec + + def set_variable_static_subdev_spec(self, variable_static_subdev_spec): + self.variable_static_subdev_spec = variable_static_subdev_spec + self._variable_static_subdev_spec_static_text.set_value(self.variable_static_subdev_spec) + + def get_variable_static_rf_freq(self): + return self.variable_static_rf_freq + + def set_variable_static_rf_freq(self, variable_static_rf_freq): + self.variable_static_rf_freq = variable_static_rf_freq + self._variable_static_rf_freq_static_text.set_value(self.variable_static_rf_freq) + + def get_variable_static_requested_freq(self): + return self.variable_static_requested_freq + + def set_variable_static_requested_freq(self, variable_static_requested_freq): + self.variable_static_requested_freq = variable_static_requested_freq + self._variable_static_requested_freq_static_text.set_value(self.variable_static_requested_freq) + + def get_variable_static_max_bin_freq(self): + return self.variable_static_max_bin_freq + + def set_variable_static_max_bin_freq(self, variable_static_max_bin_freq): + self.variable_static_max_bin_freq = variable_static_max_bin_freq + self._variable_static_max_bin_freq_static_text.set_value(self.variable_static_max_bin_freq) + + def get_variable_static_level_probe(self): + return self.variable_static_level_probe + + def set_variable_static_level_probe(self, variable_static_level_probe): + self.variable_static_level_probe = variable_static_level_probe + self._variable_static_level_probe_static_text.set_value(self.variable_static_level_probe) + + def get_variable_static_fft_window_name(self): + return self.variable_static_fft_window_name + + def set_variable_static_fft_window_name(self, variable_static_fft_window_name): + self.variable_static_fft_window_name = variable_static_fft_window_name + self._variable_static_fft_window_name_static_text.set_value(self.variable_static_fft_window_name) + + def get_variable_static_fft_max_lvl_log(self): + return self.variable_static_fft_max_lvl_log + + def set_variable_static_fft_max_lvl_log(self, variable_static_fft_max_lvl_log): + self.variable_static_fft_max_lvl_log = variable_static_fft_max_lvl_log + self._variable_static_fft_max_lvl_log_static_text.set_value(self.variable_static_fft_max_lvl_log) + + def get_variable_static_actual_sample_rate(self): + return self.variable_static_actual_sample_rate + + def set_variable_static_actual_sample_rate(self, variable_static_actual_sample_rate): + self.variable_static_actual_sample_rate = variable_static_actual_sample_rate + self._variable_static_actual_sample_rate_static_text.set_value(self.variable_static_actual_sample_rate) + + def get_variable_static_actual_dsp_freq(self): + return self.variable_static_actual_dsp_freq + + def set_variable_static_actual_dsp_freq(self, variable_static_actual_dsp_freq): + self.variable_static_actual_dsp_freq = variable_static_actual_dsp_freq + self._variable_static_actual_dsp_freq_static_text.set_value(self.variable_static_actual_dsp_freq) + + def get_variable_any_code_waterfall_ave(self): + return self.variable_any_code_waterfall_ave + + def set_variable_any_code_waterfall_ave(self, variable_any_code_waterfall_ave): + self.variable_any_code_waterfall_ave = variable_any_code_waterfall_ave + + def get_variable_any_code_iq_correction(self): + return self.variable_any_code_iq_correction + + def set_variable_any_code_iq_correction(self, variable_any_code_iq_correction): + self.variable_any_code_iq_correction = variable_any_code_iq_correction + + def get_variable_any_code_fft_sink_peak_hold(self): + return self.variable_any_code_fft_sink_peak_hold + + def set_variable_any_code_fft_sink_peak_hold(self, variable_any_code_fft_sink_peak_hold): + self.variable_any_code_fft_sink_peak_hold = variable_any_code_fft_sink_peak_hold + + def get_variable_any_code_fft_sink_ave(self): + return self.variable_any_code_fft_sink_ave + + def set_variable_any_code_fft_sink_ave(self, variable_any_code_fft_sink_ave): + self.variable_any_code_fft_sink_ave = variable_any_code_fft_sink_ave + + def get_variable_any_code_auto_dc_offset_removal(self): + return self.variable_any_code_auto_dc_offset_removal + + def set_variable_any_code_auto_dc_offset_removal(self, variable_any_code_auto_dc_offset_removal): + self.variable_any_code_auto_dc_offset_removal = variable_any_code_auto_dc_offset_removal + + def get_update_time_source(self): + return self.update_time_source + + def set_update_time_source(self, update_time_source): + self.update_time_source = update_time_source + + def get_update_clock_source(self): + return self.update_clock_source + + def set_update_clock_source(self, update_clock_source): + self.update_clock_source = update_clock_source + + def get_tune_obj(self): + return self.tune_obj + + def set_tune_obj(self, tune_obj): + self.tune_obj = tune_obj + self._run_evaluators_later(('actual_center_freq', lambda: self._evalutate_actual_center_freq(**{'tune_obj': self.tune_obj}))) + self._run_evaluators_later(('tune_result', lambda: self._evalutate_tune_result(**{'tune_obj': self.tune_obj}))) + + def get_static_locked(self): + return self.static_locked + + def set_static_locked(self, static_locked): + self.static_locked = static_locked + self._static_locked_static_text.set_value(self.static_locked) + + def get_show_stream_tags_chk(self): + return self.show_stream_tags_chk + + def set_show_stream_tags_chk(self, show_stream_tags_chk): + self.show_stream_tags_chk = show_stream_tags_chk + self._show_stream_tags_chk_check_box.set_value(self.show_stream_tags_chk) + self.blocks_tag_debug_0.set_display(self.show_stream_tags_chk) + + def get_show_max_lvl(self): + return self.show_max_lvl + + def set_show_max_lvl(self, show_max_lvl): + self.show_max_lvl = show_max_lvl + self._show_max_lvl_check_box.set_value(self.show_max_lvl) + + def get_show_max_freq(self): + return self.show_max_freq + + def set_show_max_freq(self, show_max_freq): + self.show_max_freq = show_max_freq + self._show_max_freq_check_box.set_value(self.show_max_freq) + + def get_selected_gain_proxy(self): + return self.selected_gain_proxy + + def set_selected_gain_proxy(self, selected_gain_proxy): + self.selected_gain_proxy = selected_gain_proxy + self.src.set_gain(self.selected_gain_proxy, 0) + + def get_selected_antenna_proxy(self): + return self.selected_antenna_proxy + + def set_selected_antenna_proxy(self, selected_antenna_proxy): + self.selected_antenna_proxy = selected_antenna_proxy + self.src.set_antenna(self.selected_antenna_proxy, 0) + + def get_scope_mode(self): + return self.scope_mode + + def set_scope_mode(self, scope_mode): + self.scope_mode = scope_mode + self._scope_mode_chooser.set_value(self.scope_mode) + self.blks2_selector_0.set_input_index(int(self.scope_mode)) + self.blks2_selector_0_0.set_output_index(int(self.scope_mode)) + + def get_requested_sample_rate(self): + return self.requested_sample_rate + + def set_requested_sample_rate(self, requested_sample_rate): + self.requested_sample_rate = requested_sample_rate + self._run_evaluators_later(('actual_sample_rate', lambda: self._evalutate_actual_sample_rate(**{'requested_sample_rate': self.requested_sample_rate}))) + self.src.set_samp_rate(self.requested_sample_rate) + + def get_motherboard_sensor_names(self): + return self.motherboard_sensor_names + + def set_motherboard_sensor_names(self, motherboard_sensor_names): + self.motherboard_sensor_names = motherboard_sensor_names + self._run_evaluators_later(('motherboard_sensors', lambda: self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': self.motherboard_sensor_names}))) + + def get_max_decim(self): + return self.max_decim + + def set_max_decim(self, max_decim): + self.max_decim = max_decim + + def get_initial_gain(self): + return self.initial_gain + + def set_initial_gain(self, initial_gain): + self.initial_gain = initial_gain + self._run_evaluators_later(('gain_default', lambda: self._evalutate_gain_default(**{'initial_gain': self.initial_gain}))) + + def get_has_lo_locked(self): + return self.has_lo_locked + + def set_has_lo_locked(self, has_lo_locked): + self.has_lo_locked = has_lo_locked + self.set_locked_probe_poll_rate(self.lo_check_interval * float(self.has_lo_locked)) + + def get_freq_range(self): + return self.freq_range + + def set_freq_range(self, freq_range): + self.freq_range = freq_range + + def get_fix_invalid_freq(self): + return self.fix_invalid_freq + + def set_fix_invalid_freq(self, fix_invalid_freq): + self.fix_invalid_freq = fix_invalid_freq + + def get_fft_max_lvl_value(self): + return self.fft_max_lvl_value + + def set_fft_max_lvl_value(self, fft_max_lvl_value): + self.fft_max_lvl_value = fft_max_lvl_value + + def get_fft_ave_probe(self): + return self.fft_ave_probe + + def set_fft_ave_probe(self, fft_ave_probe): + self.fft_ave_probe = fft_ave_probe + self.logpwrfft_x_0.set_avg_alpha(self.fft_ave_probe) + + def get_daughterboard_sensor_names(self): + return self.daughterboard_sensor_names + + def set_daughterboard_sensor_names(self, daughterboard_sensor_names): + self.daughterboard_sensor_names = daughterboard_sensor_names + self._run_evaluators_later(('daughterboard_sensors', lambda: self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': self.daughterboard_sensor_names}))) + + def get_clicked_freq_txt(self): + return self.clicked_freq_txt + + def set_clicked_freq_txt(self, clicked_freq_txt): + self.clicked_freq_txt = clicked_freq_txt + self._clicked_freq_txt_text_box.set_value(self.clicked_freq_txt) + + def get_auto_iq_correction(self): + return self.auto_iq_correction + + def set_auto_iq_correction(self, auto_iq_correction): + self.auto_iq_correction = auto_iq_correction + self._auto_iq_correction_check_box.set_value(self.auto_iq_correction) + self._run_evaluators_later(('variable_any_code_iq_correction', lambda: self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': self.auto_iq_correction}))) + + def get_auto_dc_offset_removal(self): + return self.auto_dc_offset_removal + + def set_auto_dc_offset_removal(self, auto_dc_offset_removal): + self.auto_dc_offset_removal = auto_dc_offset_removal + self._auto_dc_offset_removal_check_box.set_value(self.auto_dc_offset_removal) + self._run_evaluators_later(('variable_any_code_auto_dc_offset_removal', lambda: self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': self.auto_dc_offset_removal}))) + + def get_any_test_1(self): + return self.any_test_1 + + def set_any_test_1(self, any_test_1): + self.any_test_1 = any_test_1 + + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + parser.add_option("-A", "--antenna", dest="antenna", type="string", default="", + help="Set Antenna (blank for default) [default=%default]") + parser.add_option("-a", "--args", dest="args", type="string", default="", + help="Set UHD device args [default=%default]") + parser.add_option("", "--ave", dest="ave", type="eng_float", default=eng_notation.num_to_str(1*0 + 0.5), + help="Set Average FFT [default=%default]") + parser.add_option("", "--averaging", dest="averaging", type="string", default="True", + help="Set Enable FFT averaging [default=%default]") + parser.add_option("", "--bw", dest="bw", type="eng_float", default=eng_notation.num_to_str(0), + help="Set Daughterboard bandwidth (where appropriate) [default=%default]") + parser.add_option("", "--dyn-rng", dest="dyn_rng", type="eng_float", default=eng_notation.num_to_str(130), + help="Set Dynamic Range [default=%default]") + parser.add_option("", "--fft-rate", dest="fft_rate", type="intx", default=15, + help="Set FFT Rate [default=%default]") + parser.add_option("", "--fft-ref-scale", dest="fft_ref_scale", type="eng_float", default=eng_notation.num_to_str(2.0), + help="Set FFT Ref Scale (p2p) [default=%default]") + parser.add_option("", "--fft-size", dest="fft_size", type="intx", default=1024, + help="Set FFT Size [default=%default]") + parser.add_option("-f", "--freq", dest="freq", type="eng_float", default=eng_notation.num_to_str(0 + 100e6), + help="Set Frequency [default=%default]") + parser.add_option("", "--freq-fine-range", dest="freq_fine_range", type="eng_float", default=eng_notation.num_to_str(2e6), + help="Set Fine frequency slider range [default=%default]") + parser.add_option("-g", "--gain", dest="gain", type="eng_float", default=eng_notation.num_to_str(float("-inf")), + help="Set Gain (default '-inf' selects relative gain of 25%) [default=%default]") + parser.add_option("", "--lo-check-interval", dest="lo_check_interval", type="eng_float", default=eng_notation.num_to_str(5), + help="Set LO lock check frequency (Hz) [default=%default]") + parser.add_option("", "--lo-offset", dest="lo_offset", type="eng_float", default=eng_notation.num_to_str(0), + help="Set LO offset (selects LO offset tuning mode) [default=%default]") + parser.add_option("", "--mag-alpha", dest="mag_alpha", type="eng_float", default=eng_notation.num_to_str(1e-3), + help="Set Signal magnitude probe alpha [default=%default]") + parser.add_option("", "--peak-hold", dest="peak_hold", type="string", default="False", + help="Set FFT peak hold [default=%default]") + parser.add_option("", "--pps", dest="pps", type="string", default='', + help="Set Time source (none, internal, external, mimo, gpsdo). <empty> leaves it at the default. [default=%default]") + parser.add_option("", "--probe-interval", dest="probe_interval", type="eng_float", default=eng_notation.num_to_str(3), + help="Set Signal probe frequency (Hz) [default=%default]") + parser.add_option("-s", "--rate", dest="rate", type="eng_float", default=eng_notation.num_to_str(1e6), + help="Set Sample Rate [default=%default]") + parser.add_option("", "--ref", dest="ref", type="string", default='', + help="Set Clock source (internal, external, mimo, gpsdo). <empty> leaves it at the default. [default=%default]") + parser.add_option("", "--ref-lvl", dest="ref_lvl", type="eng_float", default=eng_notation.num_to_str(0), + help="Set Reference Level [default=%default]") + parser.add_option("", "--sensor-interval", dest="sensor_interval", type="eng_float", default=eng_notation.num_to_str(2), + help="Set Sensor update frequency (Hz) [default=%default]") + parser.add_option("", "--show-stream-tags", dest="show_stream_tags", type="string", default="False", + help="Set Print stream tags [default=%default]") + parser.add_option("", "--spec", dest="spec", type="string", default='', + help="Set Sub-device specification (where appropriate) [default=%default]") + parser.add_option("", "--stream-args", dest="stream_args", type="string", default="", + help="Set Stream arguments (e.g. scalar=1024) [default=%default]") + parser.add_option("", "--window", dest="window", type="string", default="auto", + help="Set Window (bh: Blackman-Harris, ham: Hamming, han: Hanning, rect: Rectangular, flat: Flattop) [default=%default]") + parser.add_option("", "--wire-format", dest="wire_format", type="string", default="", + help="Set Wire format (e.g. sc16, sc8) [blank = automatic] [default=%default]") + (options, args) = parser.parse_args() + if gr.enable_realtime_scheduling() != gr.RT_OK: + print "Error: failed to enable realtime scheduling." + tb = mega_fft(antenna=options.antenna, args=options.args, ave=options.ave, averaging=options.averaging, bw=options.bw, dyn_rng=options.dyn_rng, fft_rate=options.fft_rate, fft_ref_scale=options.fft_ref_scale, fft_size=options.fft_size, freq=options.freq, freq_fine_range=options.freq_fine_range, gain=options.gain, lo_check_interval=options.lo_check_interval, lo_offset=options.lo_offset, mag_alpha=options.mag_alpha, peak_hold=options.peak_hold, pps=options.pps, probe_interval=options.probe_interval, rate=options.rate, ref=options.ref, ref_lvl=options.ref_lvl, sensor_interval=options.sensor_interval, show_stream_tags=options.show_stream_tags, spec=options.spec, stream_args=options.stream_args, window=options.window, wire_format=options.wire_format) + tb.Start(True) + tb.Wait() diff --git a/tools/mega_fft/mega_fft_2ch.py b/tools/mega_fft/mega_fft_2ch.py new file mode 100755 index 000000000..32543cf6e --- /dev/null +++ b/tools/mega_fft/mega_fft_2ch.py @@ -0,0 +1,2819 @@ +#!/usr/bin/env python2 +################################################## +# GNU Radio Python Flow Graph +# Copyright 2014 National Instruments +# +# Title: Mega FFT +# Description: Standard edition, dual-channel +# Generated: Sun Jul 26 20:09:53 2015 +################################################## + +if __name__ == '__main__': + import ctypes + import sys + if sys.platform.startswith('linux'): + try: + x11 = ctypes.cdll.LoadLibrary('libX11.so') + x11.XInitThreads() + except: + print "Warning: failed to XInitThreads()" + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import eng_notation +from gnuradio import gr +from gnuradio import uhd +from gnuradio import wxgui +from gnuradio.eng_option import eng_option +from gnuradio.fft import logpwrfft +from gnuradio.fft import window +from gnuradio.fft import window as fft_win +from gnuradio.filter import firdes +from gnuradio.wxgui import fftsink2 +from gnuradio.wxgui import forms +from gnuradio.wxgui import scopesink2 +from gnuradio.wxgui import waterfallsink2 +from grc_gnuradio import blks2 as grc_blks2 +from grc_gnuradio import wxgui as grc_wxgui +from optparse import OptionParser +import math +import threading +import time +import time, datetime +import wx + +class mega_fft_2ch(grc_wxgui.top_block_gui): + + def __init__(self, antenna="", args="", ave=1*0 + 0.5, averaging="True", bw=0, dyn_rng=130, fft_rate=15, fft_ref_scale=2.0, fft_size=1024, freq=0 + 100e6, freq_fine_range=2e6, gain=float("-inf"), lo_check_interval=5, lo_offset=0, mag_alpha=1e-3, peak_hold="False", pps='', probe_interval=3, rate=1e6, ref='', ref_lvl=0, sensor_interval=2, show_stream_tags="False", spec='', stream_args="", window="auto", wire_format=""): + grc_wxgui.top_block_gui.__init__(self, title="Mega FFT") + + ################################################## + # Parameters + ################################################## + self.antenna = antenna + self.args = args + self.ave = ave + self.averaging = averaging + self.bw = bw + self.dyn_rng = dyn_rng + self.fft_rate = fft_rate + self.fft_ref_scale = fft_ref_scale + self.fft_size = fft_size + self.freq = freq + self.freq_fine_range = freq_fine_range + self.gain = gain + self.lo_check_interval = lo_check_interval + self.lo_offset = lo_offset + self.mag_alpha = mag_alpha + self.peak_hold = peak_hold + self.pps = pps + self.probe_interval = probe_interval + self.rate = rate + self.ref = ref + self.ref_lvl = ref_lvl + self.sensor_interval = sensor_interval + self.show_stream_tags = show_stream_tags + self.spec = spec + self.stream_args = stream_args + self.window = window + self.wire_format = wire_format + + ################################################## + # Variables + ################################################## + self.relative_freq = relative_freq = 1 + self.gain_range = gain_range = uhd.gain_range(0,0,0) + self.fft_max_idx = fft_max_idx = (fft_size/2)*0 + self.actual_sample_rate = actual_sample_rate = rate + self.actual_center_freq = actual_center_freq = 0 + self.str_to_bool = str_to_bool = lambda x: len(x)>0 and x.lower()[0] not in ['0', 'n', 'f'] + self.signal_probe = signal_probe = 0.0 + self.samp_rate = samp_rate = int(actual_sample_rate) + self.requested_freq_txt = requested_freq_txt = freq + self.gain_default = gain_default = gain_range.start() + self.freq_fine = freq_fine = 0 + self.fft_max_idx_norm = fft_max_idx_norm = [fft_max_idx,fft_max_idx-fft_size][int(fft_max_idx>(fft_size/2))] + self.fft_center_freq = fft_center_freq = actual_center_freq * relative_freq + self.antennas = antennas = [''] + self.window_fn_name_map = window_fn_name_map = {'auto': 'Auto', 'bh': 'Blackman-Harris', 'ham': 'Hamming', 'han': 'Hanning', 'rect': 'Rectangular', 'flat': 'Flattop'} + self.window_fn_map = window_fn_map = {'auto': None, 'bh': fft_win.blackmanharris, 'ham': fft_win.hamming, 'han': fft_win.hanning, 'rect': fft_win.rectangular, 'flat': fft_win.flattop} + self.usrp_info = usrp_info = '(unknown)' + self.tune_result = tune_result = uhd.tune_result_t() + self.tune_mode = tune_mode = [1,0][lo_offset==0.0] + self.time_probe = time_probe = uhd.time_spec_t() + self.test = test = 1 + self.subdev_spec = subdev_spec = '(unknown)' + self.signal_probe_log = signal_probe_log = math.log10([signal_probe,1.0][signal_probe==0.0])*10 + self.selected_gain = selected_gain = gain_default + self.selected_antenna = selected_antenna = [ [antenna,antennas[0]][antenna not in antennas] ,antennas[0]][antenna==''] + self.requested_sample_rate_base = requested_sample_rate_base = rate + self.requested_freq = requested_freq = requested_freq_txt + freq_fine + self.relative_time = relative_time = True + self.motherboard_sensors = motherboard_sensors = '(none)' + self.max_bin_freq = max_bin_freq = fft_center_freq + (((1.0*fft_max_idx_norm) / fft_size) * samp_rate) + self.locked_probe = locked_probe = '(unknown)' + self.lo_offset_txt = lo_offset_txt = lo_offset + self.fft_peak_hold = fft_peak_hold = str_to_bool(peak_hold) + self.fft_max_lvl_2 = fft_max_lvl_2 = 0.0 + self.fft_max_lvl = fft_max_lvl = 0.0 + self.fft_averaging = fft_averaging = str_to_bool(averaging) + self.decim = decim = 1 + self.daughterboard_sensors = daughterboard_sensors = '(none)' + self.clicked_freq = clicked_freq = 0 + self.window_fn = window_fn = window_fn_map[window] + self.variable_static_usrp_info = variable_static_usrp_info = usrp_info + self.variable_static_time_now = variable_static_time_now = str( [time.ctime(time_probe.get_real_secs()), datetime.timedelta(seconds=time_probe.get_real_secs()), time.gmtime(time_probe.get_real_secs())] [relative_time]) + self.variable_static_text_0_0 = variable_static_text_0_0 = daughterboard_sensors + self.variable_static_text_0 = variable_static_text_0 = motherboard_sensors + self.variable_static_subdev_spec = variable_static_subdev_spec = subdev_spec + self.variable_static_rf_freq = variable_static_rf_freq = tune_result.actual_rf_freq + self.variable_static_requested_freq = variable_static_requested_freq = requested_freq + self.variable_static_max_bin_freq = variable_static_max_bin_freq = max_bin_freq + self.variable_static_level_probe = variable_static_level_probe = signal_probe_log + self.variable_static_fft_window_name = variable_static_fft_window_name = window_fn_name_map[window] + self.variable_static_fft_max_lvl_log = variable_static_fft_max_lvl_log = fft_max_lvl + self.variable_static_actual_sample_rate = variable_static_actual_sample_rate = actual_sample_rate + self.variable_static_actual_dsp_freq = variable_static_actual_dsp_freq = tune_result.actual_dsp_freq + self.variable_any_code_waterfall_ave = variable_any_code_waterfall_ave = fft_averaging + self.variable_any_code_iq_correction = variable_any_code_iq_correction = None + self.variable_any_code_fft_sink_peak_hold = variable_any_code_fft_sink_peak_hold = fft_peak_hold + self.variable_any_code_fft_sink_ave = variable_any_code_fft_sink_ave = fft_averaging + self.variable_any_code_auto_dc_offset_removal = variable_any_code_auto_dc_offset_removal = None + self.update_time_source = update_time_source = None + self.update_clock_source = update_clock_source = None + self.tune_obj = tune_obj = [requested_freq, uhd.tune_request(requested_freq, lo_offset_txt), uhd.tune_request(requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][tune_mode] + self.static_locked = static_locked = '"' + str(locked_probe) + '"' + self.show_stream_tags_chk = show_stream_tags_chk = [False, True][show_stream_tags.lower() != 'false'] + self.show_max_lvl = show_max_lvl = True + self.show_max_freq = show_max_freq = True + self.selected_gain_proxy = selected_gain_proxy = selected_gain + self.selected_antenna_proxy = selected_antenna_proxy = selected_antenna + self.scope_mode_2 = scope_mode_2 = 0+1 + self.scope_mode = scope_mode = 0+1 + self.requested_sample_rate = requested_sample_rate = requested_sample_rate_base / (1.*decim) + self.motherboard_sensor_names = motherboard_sensor_names = [] + self.max_decim = max_decim = 256 + self.initial_gain = initial_gain = 0 + self.has_lo_locked = has_lo_locked = False + self.freq_range = freq_range = uhd.freq_range(freq,freq+1) + self.fix_invalid_freq = fix_invalid_freq = None + self.fft_max_lvl_value_2 = fft_max_lvl_value_2 = fft_max_lvl_2 + self.fft_max_lvl_value = fft_max_lvl_value = fft_max_lvl + self.fft_ave_probe = fft_ave_probe = ave + self.daughterboard_sensor_names = daughterboard_sensor_names = [] + self.clicked_freq_txt = clicked_freq_txt = clicked_freq + self.auto_iq_correction = auto_iq_correction = True + self.auto_dc_offset_removal = auto_dc_offset_removal = True + self.any_test_1 = any_test_1 = test + + ################################################## + # Blocks + ################################################## + self.src = uhd.usrp_source( + ",".join((args, "")), + uhd.stream_args( + cpu_format="fc32", + channels=range(2), + ), + + ) + if spec != "": self.src.set_subdev_spec(spec, 0) + self.src.set_samp_rate(requested_sample_rate) + self.src.set_center_freq(uhd.tune_request(freq, lo_offset), 0) + self.src.set_gain(selected_gain_proxy, 0) + self.src.set_center_freq(uhd.tune_request(freq, lo_offset), 1) + self.src.set_gain(selected_gain_proxy, 1) + self.nb_right = self.nb_right = wx.Notebook(self.GetWin(), style=wx.NB_TOP) + self.nb_right.AddPage(grc_wxgui.Panel(self.nb_right), "Params") + self.GridAdd(self.nb_right, 0, 1, 1, 1) + self.nb = self.nb = wx.Notebook(self.GetWin(), style=wx.NB_TOP) + self.nb.AddPage(grc_wxgui.Panel(self.nb), "FFT") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Scope") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Waterfall") + self.nb.AddPage(grc_wxgui.Panel(self.nb), "Sensors") + self.GridAdd(self.nb, 0, 0, 1, 1) + self.nb_test = self.nb_test = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_test.AddPage(grc_wxgui.Panel(self.nb_test), "Test") + self.nb_right.GetPage(0).Add(self.nb_test) + self.nb_analog = self.nb_analog = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_analog.AddPage(grc_wxgui.Panel(self.nb_analog), "Analog") + self.nb_analog.AddPage(grc_wxgui.Panel(self.nb_analog), "DC") + self.nb_right.GetPage(0).Add(self.nb_analog) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_gain_range(): + self._post_any_code_evaluators += [('gain_range', lambda: self._evalutate_gain_range(**{}))] + def __evalutate_gain_range(*args, **kwds): + try: + self.gain_range = self.src.get_gain_range() + self.set_gain_range(self.gain_range) + except AttributeError, e: + print "AttributeError while evaulating gain_range:", e + __post_evalutate_gain_range() + except Exception, e: + print "Exception while evaluating gain_range:", e + self._evalutate_gain_range = __evalutate_gain_range + self.__post_evalutate_gain_range = __post_evalutate_gain_range + self._evalutate_gain_range(**{}) + gain_range = self.gain_range + self.fft_sink = fftsink2.fft_sink_c( + self.nb.GetPage(0).GetWin(), + baseband_freq=fft_center_freq, + y_per_div=10, + y_divs=int(dyn_rng/10), + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=1024*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="FFT Plot", + peak_hold=False, fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(0).Add(self.fft_sink.win) + def fft_sink_callback(x, y): + self.set_clicked_freq(x) + + self.fft_sink.set_callback(fft_sink_callback) + self.waterfall_sink = waterfallsink2.waterfall_sink_c( + self.nb.GetPage(2).GetWin(), + baseband_freq=fft_center_freq, + dynamic_range=dyn_rng, + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=512*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="Waterfall Plot", + fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(2).Add(self.waterfall_sink.win) + def waterfall_sink_callback(x, y): + self.set_clicked_freq(x) + + self.waterfall_sink.set_callback(waterfall_sink_callback) + self._test_chooser = forms.button( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.test, + callback=self.set_test, + label="Test", + choices=[1], + labels=['Reset FFT average'], + ) + self.nb_test.GetPage(0).Add(self._test_chooser) + self._show_stream_tags_chk_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_stream_tags_chk, + callback=self.set_show_stream_tags_chk, + label="Print stream tags", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_stream_tags_chk_check_box, 0, 0, 1, 1) + self._scope_mode_2_chooser = forms.radio_buttons( + parent=self.nb.GetPage(1).GetWin(), + value=self.scope_mode_2, + callback=self.set_scope_mode_2, + label="Scope Mode 2", + choices=[0, 1], + labels=['Complex', 'Magnitude'], + style=wx.RA_HORIZONTAL, + ) + self.nb.GetPage(1).Add(self._scope_mode_2_chooser) + self._scope_mode_chooser = forms.radio_buttons( + parent=self.nb.GetPage(1).GetWin(), + value=self.scope_mode, + callback=self.set_scope_mode, + label="Scope Mode", + choices=[0, 1], + labels=['Complex', 'Magnitude'], + style=wx.RA_HORIZONTAL, + ) + self.nb.GetPage(1).Add(self._scope_mode_chooser) + self.probe_avg_mag = analog.probe_avg_mag_sqrd_c(0, mag_alpha) + self.nb_rate = self.nb_rate = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_rate.AddPage(grc_wxgui.Panel(self.nb_rate), "Rate") + self.nb_right.GetPage(0).Add(self.nb_rate) + self.nb_info = self.nb_info = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_info.AddPage(grc_wxgui.Panel(self.nb_info), "Info") + self.nb_right.GetPage(0).GridAdd(self.nb_info, 0, 0, 1, 1) + self.nb_freq = self.nb_freq = wx.Notebook(self.nb_right.GetPage(0).GetWin(), style=wx.NB_LEFT) + self.nb_freq.AddPage(grc_wxgui.Panel(self.nb_freq), "Freq") + self.nb_right.GetPage(0).Add(self.nb_freq) + if sensor_interval > 0: + self._motherboard_sensor_names_poll_rate = sensor_interval + else: + self._motherboard_sensor_names_poll_rate = 1 + self._motherboard_sensor_names_enabled = sensor_interval > 0 + def _set_motherboard_sensor_names_poll_rate(rate): + self._motherboard_sensor_names_enabled = rate > 0 + if rate > 0: + self._motherboard_sensor_names_poll_rate = rate + self.set_motherboard_sensor_names_poll_rate = _set_motherboard_sensor_names_poll_rate + def _motherboard_sensor_names_probe(): + while True: + if self._motherboard_sensor_names_enabled: + val = self.src.get_mboard_sensor_names() + try: + self.set_motherboard_sensor_names(val) + except AttributeError: + pass + time.sleep(1.0/(self._motherboard_sensor_names_poll_rate)) + _motherboard_sensor_names_thread = threading.Thread(target=_motherboard_sensor_names_probe) + _motherboard_sensor_names_thread.daemon = True + _motherboard_sensor_names_thread.start() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_initial_gain(): + self._post_any_code_evaluators += [('initial_gain', lambda: self._evalutate_initial_gain(**{'gain_range': gain_range}))] + def __evalutate_initial_gain(*args, **kwds): + try: + self.initial_gain = [gain,gain_range.start() + ((gain_range.stop() - gain_range.start()) * 0.25)][gain==float('-inf')] + self.set_initial_gain(self.initial_gain) + except AttributeError, e: + print "AttributeError while evaulating initial_gain:", e + __post_evalutate_initial_gain() + except Exception, e: + print "Exception while evaluating initial_gain:", e + self._evalutate_initial_gain = __evalutate_initial_gain + self.__post_evalutate_initial_gain = __post_evalutate_initial_gain + self._evalutate_initial_gain(**{'gain_range': gain_range}) + initial_gain = self.initial_gain + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_has_lo_locked(): + self._post_any_code_evaluators += [('has_lo_locked', lambda: self._evalutate_has_lo_locked(**{}))] + def __evalutate_has_lo_locked(*args, **kwds): + try: + self.has_lo_locked = 'lo_locked' in self.src.get_sensor_names() + self.set_has_lo_locked(self.has_lo_locked) + except AttributeError, e: + print "AttributeError while evaulating has_lo_locked:", e + __post_evalutate_has_lo_locked() + except Exception, e: + print "Exception while evaluating has_lo_locked:", e + self._evalutate_has_lo_locked = __evalutate_has_lo_locked + self.__post_evalutate_has_lo_locked = __post_evalutate_has_lo_locked + self._evalutate_has_lo_locked(**{}) + has_lo_locked = self.has_lo_locked + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_freq_range(): + self._post_any_code_evaluators += [('freq_range', lambda: self._evalutate_freq_range(**{}))] + def __evalutate_freq_range(*args, **kwds): + try: + self.freq_range = self.src.get_freq_range() + self.set_freq_range(self.freq_range) + except AttributeError, e: + print "AttributeError while evaulating freq_range:", e + __post_evalutate_freq_range() + except Exception, e: + print "Exception while evaluating freq_range:", e + self._evalutate_freq_range = __evalutate_freq_range + self.__post_evalutate_freq_range = __post_evalutate_freq_range + self._evalutate_freq_range(**{}) + freq_range = self.freq_range + self.fft_max_lvl_probe_2 = blocks.probe_signal_f() + if 5 > 0: + self._fft_ave_probe_poll_rate = 5 + else: + self._fft_ave_probe_poll_rate = 1 + self._fft_ave_probe_enabled = 5 > 0 + def _set_fft_ave_probe_poll_rate(rate): + self._fft_ave_probe_enabled = rate > 0 + if rate > 0: + self._fft_ave_probe_poll_rate = rate + self.set_fft_ave_probe_poll_rate = _set_fft_ave_probe_poll_rate + def _fft_ave_probe_probe(): + while True: + if self._fft_ave_probe_enabled: + val = self.fft_sink.win['avg_alpha'] * [1.0/self.fft_sink.win['avg_alpha'],1.0][self.fft_sink.win['average']];() + try: + self.set_fft_ave_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_ave_probe_poll_rate)) + _fft_ave_probe_thread = threading.Thread(target=_fft_ave_probe_probe) + _fft_ave_probe_thread.daemon = True + _fft_ave_probe_thread.start() + if sensor_interval > 0: + self._daughterboard_sensor_names_poll_rate = sensor_interval + else: + self._daughterboard_sensor_names_poll_rate = 1 + self._daughterboard_sensor_names_enabled = sensor_interval > 0 + def _set_daughterboard_sensor_names_poll_rate(rate): + self._daughterboard_sensor_names_enabled = rate > 0 + if rate > 0: + self._daughterboard_sensor_names_poll_rate = rate + self.set_daughterboard_sensor_names_poll_rate = _set_daughterboard_sensor_names_poll_rate + def _daughterboard_sensor_names_probe(): + while True: + if self._daughterboard_sensor_names_enabled: + val = self.src.get_sensor_names() + try: + self.set_daughterboard_sensor_names(val) + except AttributeError: + pass + time.sleep(1.0/(self._daughterboard_sensor_names_poll_rate)) + _daughterboard_sensor_names_thread = threading.Thread(target=_daughterboard_sensor_names_probe) + _daughterboard_sensor_names_thread.daemon = True + _daughterboard_sensor_names_thread.start() + self._auto_iq_correction_check_box = forms.check_box( + parent=self.nb_analog.GetPage(1).GetWin(), + value=self.auto_iq_correction, + callback=self.set_auto_iq_correction, + label="Auto IQ correction", + true=True, + false=False, + ) + self.nb_analog.GetPage(1).Add(self._auto_iq_correction_check_box) + self._auto_dc_offset_removal_check_box = forms.check_box( + parent=self.nb_analog.GetPage(1).GetWin(), + value=self.auto_dc_offset_removal, + callback=self.set_auto_dc_offset_removal, + label="Auto DC offset removal", + true=True, + false=False, + ) + self.nb_analog.GetPage(1).Add(self._auto_dc_offset_removal_check_box) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_antennas(): + self._post_any_code_evaluators += [('antennas', lambda: self._evalutate_antennas(**{}))] + def __evalutate_antennas(*args, **kwds): + try: + self.antennas = self.src.get_antennas() + self.set_antennas(self.antennas) + except AttributeError, e: + print "AttributeError while evaulating antennas:", e + __post_evalutate_antennas() + except Exception, e: + print "Exception while evaluating antennas:", e + self._evalutate_antennas = __evalutate_antennas + self.__post_evalutate_antennas = __post_evalutate_antennas + self._evalutate_antennas(**{}) + antennas = self.antennas + self.wxgui_scopesink2_0_0 = scopesink2.scope_sink_c( + self.nb.GetPage(1).GetWin(), + title="Scope Plot 2", + sample_rate=samp_rate, + v_scale=0, + v_offset=0, + t_scale=0, + ac_couple=False, + xy_mode=False, + num_inputs=1, + trig_mode=wxgui.TRIG_MODE_AUTO, + y_axis_label="Counts", + ) + self.nb.GetPage(1).Add(self.wxgui_scopesink2_0_0.win) + self.wxgui_scopesink2_0 = scopesink2.scope_sink_c( + self.nb.GetPage(1).GetWin(), + title="Scope Plot", + sample_rate=samp_rate, + v_scale=0, + v_offset=0, + t_scale=0, + ac_couple=False, + xy_mode=False, + num_inputs=1, + trig_mode=wxgui.TRIG_MODE_AUTO, + y_axis_label="Counts", + ) + self.nb.GetPage(1).Add(self.wxgui_scopesink2_0.win) + self.waterfall_sink_0 = waterfallsink2.waterfall_sink_c( + self.nb.GetPage(2).GetWin(), + baseband_freq=fft_center_freq, + dynamic_range=dyn_rng, + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=512*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="Waterfall Plot 2", + fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(2).Add(self.waterfall_sink_0.win) + def waterfall_sink_0_callback(x, y): + self.set_clicked_freq(x) + + self.waterfall_sink_0.set_callback(waterfall_sink_0_callback) + self._variable_static_usrp_info_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_usrp_info, + callback=self.set_variable_static_usrp_info, + label="USRP", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).GridAdd(self._variable_static_usrp_info_static_text, 0, 0, 1, 10) + self._variable_static_time_now_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_time_now, + callback=self.set_variable_static_time_now, + label="Time now", + converter=forms.str_converter(), + ) + self.nb_test.GetPage(0).GridAdd(self._variable_static_time_now_static_text, 1, 1, 1, 2) + self._variable_static_text_0_0_static_text = forms.static_text( + parent=self.nb.GetPage(3).GetWin(), + value=self.variable_static_text_0_0, + callback=self.set_variable_static_text_0_0, + label="Daughterboard", + converter=forms.str_converter(), + ) + self.nb.GetPage(3).GridAdd(self._variable_static_text_0_0_static_text, 2, 0, 1, 1) + self._variable_static_text_0_static_text = forms.static_text( + parent=self.nb.GetPage(3).GetWin(), + value=self.variable_static_text_0, + callback=self.set_variable_static_text_0, + label="Motherboard", + converter=forms.str_converter(), + ) + self.nb.GetPage(3).GridAdd(self._variable_static_text_0_static_text, 0, 0, 1, 1) + self._variable_static_subdev_spec_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_subdev_spec, + callback=self.set_variable_static_subdev_spec, + label="Sub-device", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).GridAdd(self._variable_static_subdev_spec_static_text, 1, 0, 1, 1) + self._variable_static_rf_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_rf_freq, + callback=self.set_variable_static_rf_freq, + label="Actual RF freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_rf_freq_static_text, 5, 0, 1, 1) + self._variable_static_requested_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_requested_freq, + callback=self.set_variable_static_requested_freq, + label="Requested base + fine freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_requested_freq_static_text, 3, 0, 1, 1) + self._variable_static_max_bin_freq_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_max_bin_freq, + callback=self.set_variable_static_max_bin_freq, + label="Peak freq", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_max_bin_freq_static_text) + self._variable_static_level_probe_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_level_probe, + callback=self.set_variable_static_level_probe, + label="Signal mag^2 (dB)", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_level_probe_static_text) + self._variable_static_fft_window_name_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.variable_static_fft_window_name, + callback=self.set_variable_static_fft_window_name, + label="FFT window", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).Add(self._variable_static_fft_window_name_static_text) + self._variable_static_fft_max_lvl_log_static_text = forms.static_text( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.variable_static_fft_max_lvl_log, + callback=self.set_variable_static_fft_max_lvl_log, + label="Peak magnitude (dB)", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._variable_static_fft_max_lvl_log_static_text) + self._variable_static_actual_sample_rate_static_text = forms.static_text( + parent=self.nb_rate.GetPage(0).GetWin(), + value=self.variable_static_actual_sample_rate, + callback=self.set_variable_static_actual_sample_rate, + label="Actual", + converter=forms.float_converter(), + ) + self.nb_rate.GetPage(0).GridAdd(self._variable_static_actual_sample_rate_static_text, 0, 1, 1, 1) + self._variable_static_actual_dsp_freq_static_text = forms.static_text( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.variable_static_actual_dsp_freq, + callback=self.set_variable_static_actual_dsp_freq, + label="Actual DSP freq", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._variable_static_actual_dsp_freq_static_text, 6, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_waterfall_ave(): + self._post_any_code_evaluators += [('variable_any_code_waterfall_ave', lambda: self._evalutate_variable_any_code_waterfall_ave(**{'averaging': fft_averaging}))] + def __evalutate_variable_any_code_waterfall_ave(*args, **kwds): + try: + self.variable_any_code_waterfall_ave = self.waterfall_sink.win['average'] = fft_averaging + self.set_variable_any_code_waterfall_ave(self.variable_any_code_waterfall_ave) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_waterfall_ave:", e + __post_evalutate_variable_any_code_waterfall_ave() + except Exception, e: + print "Exception while evaluating variable_any_code_waterfall_ave:", e + self._evalutate_variable_any_code_waterfall_ave = __evalutate_variable_any_code_waterfall_ave + self.__post_evalutate_variable_any_code_waterfall_ave = __post_evalutate_variable_any_code_waterfall_ave + self.__post_evalutate_variable_any_code_waterfall_ave() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_iq_correction(): + self._post_any_code_evaluators += [('variable_any_code_iq_correction', lambda: self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': auto_iq_correction}))] + def __evalutate_variable_any_code_iq_correction(*args, **kwds): + try: + [self.src.set_auto_iq_balance(self.auto_iq_correction), self.auto_iq_correction or self.src.set_iq_balance(0+0j)] + self.set_variable_any_code_iq_correction(self.variable_any_code_iq_correction) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_iq_correction:", e + __post_evalutate_variable_any_code_iq_correction() + except Exception, e: + print "Exception while evaluating variable_any_code_iq_correction:", e + self._evalutate_variable_any_code_iq_correction = __evalutate_variable_any_code_iq_correction + self.__post_evalutate_variable_any_code_iq_correction = __post_evalutate_variable_any_code_iq_correction + self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': auto_iq_correction}) + variable_any_code_iq_correction = self.variable_any_code_iq_correction + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_fft_sink_peak_hold(): + self._post_any_code_evaluators += [('variable_any_code_fft_sink_peak_hold', lambda: self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': fft_peak_hold}))] + def __evalutate_variable_any_code_fft_sink_peak_hold(*args, **kwds): + try: + self.variable_any_code_fft_sink_peak_hold = self.fft_sink.win['peak_hold'] = fft_peak_hold + self.set_variable_any_code_fft_sink_peak_hold(self.variable_any_code_fft_sink_peak_hold) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_fft_sink_peak_hold:", e + __post_evalutate_variable_any_code_fft_sink_peak_hold() + except Exception, e: + print "Exception while evaluating variable_any_code_fft_sink_peak_hold:", e + self._evalutate_variable_any_code_fft_sink_peak_hold = __evalutate_variable_any_code_fft_sink_peak_hold + self.__post_evalutate_variable_any_code_fft_sink_peak_hold = __post_evalutate_variable_any_code_fft_sink_peak_hold + self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': fft_peak_hold}) + variable_any_code_fft_sink_peak_hold = self.variable_any_code_fft_sink_peak_hold + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_fft_sink_ave(): + self._post_any_code_evaluators += [('variable_any_code_fft_sink_ave', lambda: self._evalutate_variable_any_code_fft_sink_ave(**{'averaging': fft_averaging}))] + def __evalutate_variable_any_code_fft_sink_ave(*args, **kwds): + try: + self.variable_any_code_fft_sink_ave = self.fft_sink.win['average'] = fft_averaging + self.set_variable_any_code_fft_sink_ave(self.variable_any_code_fft_sink_ave) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_fft_sink_ave:", e + __post_evalutate_variable_any_code_fft_sink_ave() + except Exception, e: + print "Exception while evaluating variable_any_code_fft_sink_ave:", e + self._evalutate_variable_any_code_fft_sink_ave = __evalutate_variable_any_code_fft_sink_ave + self.__post_evalutate_variable_any_code_fft_sink_ave = __post_evalutate_variable_any_code_fft_sink_ave + self.__post_evalutate_variable_any_code_fft_sink_ave() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_variable_any_code_auto_dc_offset_removal(): + self._post_any_code_evaluators += [('variable_any_code_auto_dc_offset_removal', lambda: self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': auto_dc_offset_removal}))] + def __evalutate_variable_any_code_auto_dc_offset_removal(*args, **kwds): + try: + [self.src.set_auto_dc_offset(self.auto_dc_offset_removal), self.auto_dc_offset_removal or self.src.set_dc_offset(0+0j)] + self.set_variable_any_code_auto_dc_offset_removal(self.variable_any_code_auto_dc_offset_removal) + except AttributeError, e: + print "AttributeError while evaulating variable_any_code_auto_dc_offset_removal:", e + __post_evalutate_variable_any_code_auto_dc_offset_removal() + except Exception, e: + print "Exception while evaluating variable_any_code_auto_dc_offset_removal:", e + self._evalutate_variable_any_code_auto_dc_offset_removal = __evalutate_variable_any_code_auto_dc_offset_removal + self.__post_evalutate_variable_any_code_auto_dc_offset_removal = __post_evalutate_variable_any_code_auto_dc_offset_removal + self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': auto_dc_offset_removal}) + variable_any_code_auto_dc_offset_removal = self.variable_any_code_auto_dc_offset_removal + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_usrp_info(): + self._post_any_code_evaluators += [('usrp_info', lambda: self._evalutate_usrp_info(**{}))] + def __evalutate_usrp_info(*args, **kwds): + try: + self.usrp_info = '%s (\'%s\'), %s' % (self.src.get_usrp_info().get('mboard_id'), self.src.get_usrp_info().get('mboard_name'), self.src.get_usrp_info().get('rx_subdev_name')) + self.set_usrp_info(self.usrp_info) + except AttributeError, e: + print "AttributeError while evaulating usrp_info:", e + __post_evalutate_usrp_info() + except Exception, e: + print "Exception while evaluating usrp_info:", e + self._evalutate_usrp_info = __evalutate_usrp_info + self.__post_evalutate_usrp_info = __post_evalutate_usrp_info + self._evalutate_usrp_info(**{}) + usrp_info = self.usrp_info + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_update_time_source(): + self._post_any_code_evaluators += [('update_time_source', lambda: self._evalutate_update_time_source(**{'pps': pps}))] + def __evalutate_update_time_source(*args, **kwds): + try: + if self.pps != '': self.src.set_time_source(self.pps, 0); print '1 PPS =', self.pps; + self.set_update_time_source(self.update_time_source) + except AttributeError, e: + print "AttributeError while evaulating update_time_source:", e + __post_evalutate_update_time_source() + except Exception, e: + print "Exception while evaluating update_time_source:", e + self._evalutate_update_time_source = __evalutate_update_time_source + self.__post_evalutate_update_time_source = __post_evalutate_update_time_source + self._evalutate_update_time_source(**{'pps': pps}) + update_time_source = self.update_time_source + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_update_clock_source(): + self._post_any_code_evaluators += [('update_clock_source', lambda: self._evalutate_update_clock_source(**{'ref': ref}))] + def __evalutate_update_clock_source(*args, **kwds): + try: + if self.ref != '': self.src.set_clock_source(self.ref, 0); print 'Ref =', self.ref; + self.set_update_clock_source(self.update_clock_source) + except AttributeError, e: + print "AttributeError while evaulating update_clock_source:", e + __post_evalutate_update_clock_source() + except Exception, e: + print "Exception while evaluating update_clock_source:", e + self._evalutate_update_clock_source = __evalutate_update_clock_source + self.__post_evalutate_update_clock_source = __post_evalutate_update_clock_source + self._evalutate_update_clock_source(**{'ref': ref}) + update_clock_source = self.update_clock_source + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_tune_result(): + self._post_any_code_evaluators += [('tune_result', lambda: self._evalutate_tune_result(**{'tune_obj': tune_obj}))] + def __evalutate_tune_result(*args, **kwds): + try: + self.tune_result = self.src.set_center_freq(self.tune_obj) + self.set_tune_result(self.tune_result) + except AttributeError, e: + print "AttributeError while evaulating tune_result:", e + __post_evalutate_tune_result() + except Exception, e: + print "Exception while evaluating tune_result:", e + self._evalutate_tune_result = __evalutate_tune_result + self.__post_evalutate_tune_result = __post_evalutate_tune_result + self._evalutate_tune_result(**{'tune_obj': tune_obj}) + tune_result = self.tune_result + self._tune_mode_chooser = forms.drop_down( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.tune_mode, + callback=self.set_tune_mode, + label="Tune mode", + choices=[0, 1, 2], + labels=['Auto (no LO offset)', 'Auto with LO offset', 'Manual (no DSP)'], + ) + self.nb_freq.GetPage(0).GridAdd(self._tune_mode_chooser, 0, 0, 1, 1) + if sensor_interval > 0: + self._time_probe_poll_rate = sensor_interval + else: + self._time_probe_poll_rate = 1 + self._time_probe_enabled = sensor_interval > 0 + def _set_time_probe_poll_rate(rate): + self._time_probe_enabled = rate > 0 + if rate > 0: + self._time_probe_poll_rate = rate + self.set_time_probe_poll_rate = _set_time_probe_poll_rate + def _time_probe_probe(): + while True: + if self._time_probe_enabled: + val = self.src.get_time_now() + try: + self.set_time_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._time_probe_poll_rate)) + _time_probe_thread = threading.Thread(target=_time_probe_probe) + _time_probe_thread.daemon = True + _time_probe_thread.start() + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_subdev_spec(): + self._post_any_code_evaluators += [('subdev_spec', lambda: self._evalutate_subdev_spec(**{}))] + def __evalutate_subdev_spec(*args, **kwds): + try: + self.subdev_spec = '[' + self.src.get_subdev_spec().strip() + ']' + self.set_subdev_spec(self.subdev_spec) + except AttributeError, e: + print "AttributeError while evaulating subdev_spec:", e + __post_evalutate_subdev_spec() + except Exception, e: + print "Exception while evaluating subdev_spec:", e + self._evalutate_subdev_spec = __evalutate_subdev_spec + self.__post_evalutate_subdev_spec = __post_evalutate_subdev_spec + self._evalutate_subdev_spec(**{}) + subdev_spec = self.subdev_spec + self._static_locked_static_text = forms.static_text( + parent=self.nb_info.GetPage(0).GetWin(), + value=self.static_locked, + callback=self.set_static_locked, + label="Locked", + converter=forms.str_converter(), + ) + self.nb_info.GetPage(0).Add(self._static_locked_static_text) + if probe_interval > 0: + self._signal_probe_poll_rate = probe_interval + else: + self._signal_probe_poll_rate = 1 + self._signal_probe_enabled = probe_interval > 0 + def _set_signal_probe_poll_rate(rate): + self._signal_probe_enabled = rate > 0 + if rate > 0: + self._signal_probe_poll_rate = rate + self.set_signal_probe_poll_rate = _set_signal_probe_poll_rate + def _signal_probe_probe(): + while True: + if self._signal_probe_enabled: + val = self.probe_avg_mag.level() + try: + self.set_signal_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._signal_probe_poll_rate)) + _signal_probe_thread = threading.Thread(target=_signal_probe_probe) + _signal_probe_thread.daemon = True + _signal_probe_thread.start() + self._show_max_lvl_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_max_lvl, + callback=self.set_show_max_lvl, + label="Show max level", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_max_lvl_check_box, 0, 2, 1, 1) + self._show_max_freq_check_box = forms.check_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.show_max_freq, + callback=self.set_show_max_freq, + label="Show max freq", + true=True, + false=False, + ) + self.nb_test.GetPage(0).GridAdd(self._show_max_freq_check_box, 0, 1, 1, 1) + _selected_gain_sizer = wx.BoxSizer(wx.VERTICAL) + self._selected_gain_text_box = forms.text_box( + parent=self.nb_analog.GetPage(0).GetWin(), + sizer=_selected_gain_sizer, + value=self.selected_gain, + callback=self.set_selected_gain, + label="Gain", + converter=forms.float_converter(), + proportion=0, + ) + self._selected_gain_slider = forms.slider( + parent=self.nb_analog.GetPage(0).GetWin(), + sizer=_selected_gain_sizer, + value=self.selected_gain, + callback=self.set_selected_gain, + minimum=gain_range.start(), + maximum=[gain_range.stop(), gain_range.start() + 1.0][gain_range.stop()==gain_range.start()], + num_steps=[int((abs(gain_range.stop()-gain_range.start())/[gain_range.step(), 1.0][gain_range.step()==0])), 1][gain_range.stop()==gain_range.start()], + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_analog.GetPage(0).Add(_selected_gain_sizer) + self._selected_antenna_chooser = forms.radio_buttons( + parent=self.nb_analog.GetPage(0).GetWin(), + value=self.selected_antenna, + callback=self.set_selected_antenna, + label="Antenna", + choices=antennas, + labels=[antennas,['(default)']][antennas==('',)], + style=wx.RA_HORIZONTAL, + ) + self.nb_analog.GetPage(0).Add(self._selected_antenna_chooser) + self._requested_sample_rate_base_text_box = forms.text_box( + parent=self.nb_rate.GetPage(0).GetWin(), + value=self.requested_sample_rate_base, + callback=self.set_requested_sample_rate_base, + label="Requested base sample rate", + converter=forms.float_converter(), + ) + self.nb_rate.GetPage(0).GridAdd(self._requested_sample_rate_base_text_box, 0, 0, 1, 1) + _requested_freq_txt_sizer = wx.BoxSizer(wx.VERTICAL) + self._requested_freq_txt_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_requested_freq_txt_sizer, + value=self.requested_freq_txt, + callback=self.set_requested_freq_txt, + label="Requested freq", + converter=forms.float_converter(), + proportion=0, + ) + self._requested_freq_txt_slider = forms.slider( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_requested_freq_txt_sizer, + value=self.requested_freq_txt, + callback=self.set_requested_freq_txt, + minimum=freq_range.start(), + maximum=[freq_range.stop(), freq_range.start() + 1.0][freq_range.start()==freq_range.stop()], + num_steps=[1000,1][freq_range.start()==freq_range.stop()], + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_freq.GetPage(0).GridAdd(_requested_freq_txt_sizer, 1, 0, 1, 1) + self._relative_time_chooser = forms.drop_down( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.relative_time, + callback=self.set_relative_time, + label="Time display", + choices=[False, True], + labels=['Absolute', 'Relative'], + ) + self.nb_test.GetPage(0).GridAdd(self._relative_time_chooser, 1, 0, 1, 1) + self._relative_freq_chooser = forms.drop_down( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.relative_freq, + callback=self.set_relative_freq, + label="Frequency Axis", + choices=[1, 0], + labels=['RF', 'Baseband'], + ) + self.nb_freq.GetPage(0).GridAdd(self._relative_freq_chooser, 7, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_motherboard_sensors(): + self._post_any_code_evaluators += [('motherboard_sensors', lambda: self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': motherboard_sensor_names}))] + def __evalutate_motherboard_sensors(*args, **kwds): + try: + self.motherboard_sensors = '\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_mboard_sensor(x))), filter(lambda x: x.find('gps') != 0, self.motherboard_sensor_names))) + self.set_motherboard_sensors(self.motherboard_sensors) + except AttributeError, e: + print "AttributeError while evaulating motherboard_sensors:", e + __post_evalutate_motherboard_sensors() + except Exception, e: + print "Exception while evaluating motherboard_sensors:", e + self._evalutate_motherboard_sensors = __evalutate_motherboard_sensors + self.__post_evalutate_motherboard_sensors = __post_evalutate_motherboard_sensors + self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': motherboard_sensor_names}) + motherboard_sensors = self.motherboard_sensors + self.logpwrfft_x_0_0 = logpwrfft.logpwrfft_c( + sample_rate=samp_rate, + fft_size=fft_size, + ref_scale=fft_ref_scale, + frame_rate=fft_rate, + avg_alpha=fft_ave_probe, + average=False, + ) + self.logpwrfft_x_0 = logpwrfft.logpwrfft_c( + sample_rate=samp_rate, + fft_size=fft_size, + ref_scale=fft_ref_scale, + frame_rate=fft_rate, + avg_alpha=fft_ave_probe, + average=False, + ) + if lo_check_interval * float(has_lo_locked) > 0: + self._locked_probe_poll_rate = lo_check_interval * float(has_lo_locked) + else: + self._locked_probe_poll_rate = 1 + self._locked_probe_enabled = lo_check_interval * float(has_lo_locked) > 0 + def _set_locked_probe_poll_rate(rate): + self._locked_probe_enabled = rate > 0 + if rate > 0: + self._locked_probe_poll_rate = rate + self.set_locked_probe_poll_rate = _set_locked_probe_poll_rate + def _locked_probe_probe(): + while True: + if self._locked_probe_enabled: + val = self.src.get_sensor('lo_locked') + try: + self.set_locked_probe(val) + except AttributeError: + pass + time.sleep(1.0/(self._locked_probe_poll_rate)) + _locked_probe_thread = threading.Thread(target=_locked_probe_probe) + _locked_probe_thread.daemon = True + _locked_probe_thread.start() + self._lo_offset_txt_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + value=self.lo_offset_txt, + callback=self.set_lo_offset_txt, + label="LO offset (only for LO offset tuning mode)", + converter=forms.float_converter(), + ) + self.nb_freq.GetPage(0).GridAdd(self._lo_offset_txt_text_box, 4, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_gain_default(): + self._post_any_code_evaluators += [('gain_default', lambda: self._evalutate_gain_default(**{'initial_gain': initial_gain}))] + def __evalutate_gain_default(*args, **kwds): + try: + self.gain_default = [gain,initial_gain][gain==float('-inf')] + self.set_gain_default(self.gain_default) + except AttributeError, e: + print "AttributeError while evaulating gain_default:", e + __post_evalutate_gain_default() + except Exception, e: + print "Exception while evaluating gain_default:", e + self._evalutate_gain_default = __evalutate_gain_default + self.__post_evalutate_gain_default = __post_evalutate_gain_default + self._evalutate_gain_default(**{'initial_gain': initial_gain}) + gain_default = self.gain_default + _freq_fine_sizer = wx.BoxSizer(wx.VERTICAL) + self._freq_fine_text_box = forms.text_box( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_freq_fine_sizer, + value=self.freq_fine, + callback=self.set_freq_fine, + label="Freq (fine)", + converter=forms.float_converter(), + proportion=0, + ) + self._freq_fine_slider = forms.slider( + parent=self.nb_freq.GetPage(0).GetWin(), + sizer=_freq_fine_sizer, + value=self.freq_fine, + callback=self.set_freq_fine, + minimum=-freq_fine_range/2, + maximum=freq_fine_range/2, + num_steps=1000, + style=wx.SL_HORIZONTAL, + cast=float, + proportion=1, + ) + self.nb_freq.GetPage(0).GridAdd(_freq_fine_sizer, 2, 0, 1, 1) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_fix_invalid_freq(): + self._post_any_code_evaluators += [('fix_invalid_freq', lambda: self._evalutate_fix_invalid_freq(**{}))] + def __evalutate_fix_invalid_freq(*args, **kwds): + try: + ((freq <= freq_range.stop()) and (freq >= freq_range.start())) or self.set_requested_freq_txt(self.src.get_center_freq()); print self.src.get_center_freq() + self.set_fix_invalid_freq(self.fix_invalid_freq) + except AttributeError, e: + print "AttributeError while evaulating fix_invalid_freq:", e + __post_evalutate_fix_invalid_freq() + except Exception, e: + print "Exception while evaluating fix_invalid_freq:", e + self._evalutate_fix_invalid_freq = __evalutate_fix_invalid_freq + self.__post_evalutate_fix_invalid_freq = __post_evalutate_fix_invalid_freq + self.__post_evalutate_fix_invalid_freq() + self.fft_sink_two_1 = fftsink2.fft_sink_c( + self.nb.GetPage(0).GetWin(), + baseband_freq=fft_center_freq, + y_per_div=10, + y_divs=int(dyn_rng/10), + ref_level=ref_lvl, + ref_scale=fft_ref_scale, + sample_rate=samp_rate, + fft_size=1024*0 + fft_size, + fft_rate=fft_rate, + average=True, + avg_alpha=ave, + title="FFT Plot 2", + peak_hold=False, fft_in=False, + always_run=False, + fft_out=False, + ) + self.nb.GetPage(0).Add(self.fft_sink_two_1.win) + def fft_sink_two_1_callback(x, y): + self.set_clicked_freq(x) + + self.fft_sink_two_1.set_callback(fft_sink_two_1_callback) + self.fft_max_lvl_probe = blocks.probe_signal_f() + if probe_interval > 0: + self._fft_max_lvl_2_poll_rate = probe_interval + else: + self._fft_max_lvl_2_poll_rate = 1 + self._fft_max_lvl_2_enabled = probe_interval > 0 + def _set_fft_max_lvl_2_poll_rate(rate): + self._fft_max_lvl_2_enabled = rate > 0 + if rate > 0: + self._fft_max_lvl_2_poll_rate = rate + self.set_fft_max_lvl_2_poll_rate = _set_fft_max_lvl_2_poll_rate + def _fft_max_lvl_2_probe(): + while True: + if self._fft_max_lvl_2_enabled: + val = self.fft_max_lvl_probe_2.level() + try: + self.set_fft_max_lvl_2(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_max_lvl_2_poll_rate)) + _fft_max_lvl_2_thread = threading.Thread(target=_fft_max_lvl_2_probe) + _fft_max_lvl_2_thread.daemon = True + _fft_max_lvl_2_thread.start() + if probe_interval > 0: + self._fft_max_lvl_poll_rate = probe_interval + else: + self._fft_max_lvl_poll_rate = 1 + self._fft_max_lvl_enabled = probe_interval > 0 + def _set_fft_max_lvl_poll_rate(rate): + self._fft_max_lvl_enabled = rate > 0 + if rate > 0: + self._fft_max_lvl_poll_rate = rate + self.set_fft_max_lvl_poll_rate = _set_fft_max_lvl_poll_rate + def _fft_max_lvl_probe(): + while True: + if self._fft_max_lvl_enabled: + val = self.fft_max_lvl_probe.level() + try: + self.set_fft_max_lvl(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_max_lvl_poll_rate)) + _fft_max_lvl_thread = threading.Thread(target=_fft_max_lvl_probe) + _fft_max_lvl_thread.daemon = True + _fft_max_lvl_thread.start() + self.fft_max_idx_probe_0 = blocks.probe_signal_s() + self.fft_max_idx_probe = blocks.probe_signal_s() + if probe_interval > 0: + self._fft_max_idx_poll_rate = probe_interval + else: + self._fft_max_idx_poll_rate = 1 + self._fft_max_idx_enabled = probe_interval > 0 + def _set_fft_max_idx_poll_rate(rate): + self._fft_max_idx_enabled = rate > 0 + if rate > 0: + self._fft_max_idx_poll_rate = rate + self.set_fft_max_idx_poll_rate = _set_fft_max_idx_poll_rate + def _fft_max_idx_probe(): + while True: + if self._fft_max_idx_enabled: + val = self.fft_max_idx_probe.level() + try: + self.set_fft_max_idx(val) + except AttributeError: + pass + time.sleep(1.0/(self._fft_max_idx_poll_rate)) + _fft_max_idx_thread = threading.Thread(target=_fft_max_idx_probe) + _fft_max_idx_thread.daemon = True + _fft_max_idx_thread.start() + _decim_sizer = wx.BoxSizer(wx.VERTICAL) + self._decim_text_box = forms.text_box( + parent=self.nb_rate.GetPage(0).GetWin(), + sizer=_decim_sizer, + value=self.decim, + callback=self.set_decim, + label="Divide base sample rate", + converter=forms.int_converter(), + proportion=0, + ) + self._decim_slider = forms.slider( + parent=self.nb_rate.GetPage(0).GetWin(), + sizer=_decim_sizer, + value=self.decim, + callback=self.set_decim, + minimum=1, + maximum=max_decim, + num_steps=max_decim-1, + style=wx.SL_HORIZONTAL, + cast=int, + proportion=1, + ) + self.nb_rate.GetPage(0).Add(_decim_sizer) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_daughterboard_sensors(): + self._post_any_code_evaluators += [('daughterboard_sensors', lambda: self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': daughterboard_sensor_names}))] + def __evalutate_daughterboard_sensors(*args, **kwds): + try: + self.daughterboard_sensors = '\n'.join(map(lambda x: '%s: %s' % (x, str(self.src.get_sensor(x))), self.daughterboard_sensor_names)) + self.set_daughterboard_sensors(self.daughterboard_sensors) + except AttributeError, e: + print "AttributeError while evaulating daughterboard_sensors:", e + __post_evalutate_daughterboard_sensors() + except Exception, e: + print "Exception while evaluating daughterboard_sensors:", e + self._evalutate_daughterboard_sensors = __evalutate_daughterboard_sensors + self.__post_evalutate_daughterboard_sensors = __post_evalutate_daughterboard_sensors + self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': daughterboard_sensor_names}) + daughterboard_sensors = self.daughterboard_sensors + self._clicked_freq_txt_text_box = forms.text_box( + parent=self.nb_test.GetPage(0).GetWin(), + value=self.clicked_freq_txt, + callback=self.set_clicked_freq_txt, + label="Clicked freq", + converter=forms.float_converter(), + ) + self.nb_test.GetPage(0).Add(self._clicked_freq_txt_text_box) + self.blocks_tag_debug_0 = blocks.tag_debug(gr.sizeof_gr_complex*1, "", ""); self.blocks_tag_debug_0.set_display(show_stream_tags_chk) + self.blocks_null_sink_0_0 = blocks.null_sink(gr.sizeof_short*1) + self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_short*1) + self.blocks_max_xx_0_0 = blocks.max_ff(fft_size) + self.blocks_max_xx_0 = blocks.max_ff(fft_size) + self.blocks_float_to_complex_0_0 = blocks.float_to_complex(1) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.blocks_complex_to_mag_0_0 = blocks.complex_to_mag(1) + self.blocks_complex_to_mag_0 = blocks.complex_to_mag(1) + self.blocks_argmax_xx_0_0 = blocks.argmax_fs(fft_size) + self.blocks_argmax_xx_0 = blocks.argmax_fs(fft_size) + self.blks2_selector_0_1 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=2, + num_outputs=1, + input_index=scope_mode_2, + output_index=0, + ) + self.blks2_selector_0_0_0 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=1, + num_outputs=2, + input_index=0, + output_index=scope_mode_2, + ) + self.blks2_selector_0_0 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=1, + num_outputs=2, + input_index=0, + output_index=scope_mode, + ) + self.blks2_selector_0 = grc_blks2.selector( + item_size=gr.sizeof_gr_complex*1, + num_inputs=2, + num_outputs=1, + input_index=scope_mode, + output_index=0, + ) + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_any_test_1(): + self._post_any_code_evaluators += [('any_test_1', lambda: self._evalutate_any_test_1(**{'test': test}))] + def __evalutate_any_test_1(*args, **kwds): + try: + if self.fft_sink.controller['average']: self.fft_sink.controller['average'] = False; time.sleep(0.25); self.fft_sink.controller['average'] = True; + self.set_any_test_1(self.any_test_1) + except AttributeError, e: + print "AttributeError while evaulating any_test_1:", e + __post_evalutate_any_test_1() + except Exception, e: + print "Exception while evaluating any_test_1:", e + self._evalutate_any_test_1 = __evalutate_any_test_1 + self.__post_evalutate_any_test_1 = __post_evalutate_any_test_1 + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_actual_sample_rate(): + self._post_any_code_evaluators += [('actual_sample_rate', lambda: self._evalutate_actual_sample_rate(**{'requested_sample_rate': requested_sample_rate}))] + def __evalutate_actual_sample_rate(*args, **kwds): + try: + self.actual_sample_rate = self.src.get_samp_rate() + self.set_actual_sample_rate(self.actual_sample_rate) + except AttributeError, e: + print "AttributeError while evaulating actual_sample_rate:", e + __post_evalutate_actual_sample_rate() + except Exception, e: + print "Exception while evaluating actual_sample_rate:", e + self._evalutate_actual_sample_rate = __evalutate_actual_sample_rate + self.__post_evalutate_actual_sample_rate = __post_evalutate_actual_sample_rate + self._evalutate_actual_sample_rate(**{'requested_sample_rate': requested_sample_rate}) + actual_sample_rate = self.actual_sample_rate + if not hasattr(self, '_post_any_code_evaluators'): + self._post_any_code_evaluators = [] + self.wxEVT_AnyCode = wxEVT_AnyCode = wx.NewEventType() + def _run_evaluators(event): + _post_any_code_evaluators = self._post_any_code_evaluators + if len(_post_any_code_evaluators) > 0: + for id, evaluator in _post_any_code_evaluators: + try: + evaluator() + except Exception, e: + print "Exception while running Any Code evaluator for '%s':" % (id), e + del _post_any_code_evaluators[0:len(_post_any_code_evaluators)] + self._run_evaluators = _run_evaluators + try: + self.GetWin().Connect(-1, -1, wxEVT_AnyCode, _run_evaluators) + except: + pass # FIXME + def _run_evaluators_later(evaluator=None): + if evaluator is not None: + self._post_any_code_evaluators += [evaluator] + try: + de = wx.PyEvent() + de.SetEventType(wxEVT_AnyCode) + wx.PostEvent(self.GetWin(), de) + except TypeError: + pass + except AttributeError: # FIXME + print "Cannot post message" + self._run_evaluators_later = _run_evaluators_later + _run_evaluators_later() + def __post_evalutate_actual_center_freq(): + self._post_any_code_evaluators += [('actual_center_freq', lambda: self._evalutate_actual_center_freq(**{'tune_obj': tune_obj}))] + def __evalutate_actual_center_freq(*args, **kwds): + try: + self.actual_center_freq = self.src.get_center_freq() + self.set_actual_center_freq(self.actual_center_freq) + except AttributeError, e: + print "AttributeError while evaulating actual_center_freq:", e + __post_evalutate_actual_center_freq() + except Exception, e: + print "Exception while evaluating actual_center_freq:", e + self._evalutate_actual_center_freq = __evalutate_actual_center_freq + self.__post_evalutate_actual_center_freq = __post_evalutate_actual_center_freq + self._evalutate_actual_center_freq(**{'tune_obj': tune_obj}) + actual_center_freq = self.actual_center_freq + + ################################################## + # Connections + ################################################## + self.connect((self.blks2_selector_0, 0), (self.wxgui_scopesink2_0, 0)) + self.connect((self.blks2_selector_0_0, 0), (self.blks2_selector_0, 0)) + self.connect((self.blks2_selector_0_0, 1), (self.blocks_complex_to_mag_0, 0)) + self.connect((self.blks2_selector_0_0_0, 0), (self.blks2_selector_0_1, 0)) + self.connect((self.blks2_selector_0_0_0, 1), (self.blocks_complex_to_mag_0_0, 0)) + self.connect((self.blks2_selector_0_1, 0), (self.wxgui_scopesink2_0_0, 0)) + self.connect((self.blocks_argmax_xx_0, 1), (self.blocks_null_sink_0, 0)) + self.connect((self.blocks_argmax_xx_0, 0), (self.fft_max_idx_probe, 0)) + self.connect((self.blocks_argmax_xx_0_0, 1), (self.blocks_null_sink_0_0, 0)) + self.connect((self.blocks_argmax_xx_0_0, 0), (self.fft_max_idx_probe_0, 0)) + self.connect((self.blocks_complex_to_mag_0, 0), (self.blocks_float_to_complex_0, 1)) + self.connect((self.blocks_complex_to_mag_0, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_complex_to_mag_0_0, 0), (self.blocks_float_to_complex_0_0, 1)) + self.connect((self.blocks_complex_to_mag_0_0, 0), (self.blocks_float_to_complex_0_0, 0)) + self.connect((self.blocks_float_to_complex_0, 0), (self.blks2_selector_0, 1)) + self.connect((self.blocks_float_to_complex_0_0, 0), (self.blks2_selector_0_1, 1)) + self.connect((self.blocks_max_xx_0, 0), (self.fft_max_lvl_probe, 0)) + self.connect((self.blocks_max_xx_0_0, 0), (self.fft_max_lvl_probe_2, 0)) + self.connect((self.logpwrfft_x_0, 0), (self.blocks_argmax_xx_0, 0)) + self.connect((self.logpwrfft_x_0, 0), (self.blocks_max_xx_0, 0)) + self.connect((self.logpwrfft_x_0_0, 0), (self.blocks_argmax_xx_0_0, 0)) + self.connect((self.logpwrfft_x_0_0, 0), (self.blocks_max_xx_0_0, 0)) + self.connect((self.src, 0), (self.blocks_tag_debug_0, 0)) + self.connect((self.src, 0), (self.blks2_selector_0_0, 0)) + self.connect((self.src, 0), (self.fft_sink, 0)) + self.connect((self.src, 0), (self.waterfall_sink, 0)) + self.connect((self.src, 0), (self.probe_avg_mag, 0)) + self.connect((self.src, 0), (self.logpwrfft_x_0, 0)) + self.connect((self.src, 1), (self.blks2_selector_0_0_0, 0)) + self.connect((self.src, 1), (self.fft_sink_two_1, 0)) + self.connect((self.src, 1), (self.logpwrfft_x_0_0, 0)) + self.connect((self.src, 1), (self.waterfall_sink_0, 0)) + + + def get_antenna(self): + return self.antenna + + def set_antenna(self, antenna): + self.antenna = antenna + self.set_selected_antenna([ [self.antenna,self.antennas[0]][self.antenna not in self.antennas] ,self.antennas[0]][self.antenna=='']) + + def get_args(self): + return self.args + + def set_args(self, args): + self.args = args + + def get_ave(self): + return self.ave + + def set_ave(self, ave): + self.ave = ave + self.set_fft_ave_probe(self.ave) + + def get_averaging(self): + return self.averaging + + def set_averaging(self, averaging): + self.averaging = averaging + self.set_fft_averaging(self.str_to_bool(self.averaging)) + + def get_bw(self): + return self.bw + + def set_bw(self, bw): + self.bw = bw + self.src.set_bandwidth(self.bw, 0) + self.src.set_bandwidth(self.bw, 1) + + def get_dyn_rng(self): + return self.dyn_rng + + def set_dyn_rng(self, dyn_rng): + self.dyn_rng = dyn_rng + + def get_fft_rate(self): + return self.fft_rate + + def set_fft_rate(self, fft_rate): + self.fft_rate = fft_rate + + def get_fft_ref_scale(self): + return self.fft_ref_scale + + def set_fft_ref_scale(self, fft_ref_scale): + self.fft_ref_scale = fft_ref_scale + + def get_fft_size(self): + return self.fft_size + + def set_fft_size(self, fft_size): + self.fft_size = fft_size + self.set_fft_max_idx((self.fft_size/2)*0) + self.set_fft_max_idx_norm([self.fft_max_idx,self.fft_max_idx-self.fft_size][int(self.fft_max_idx>(self.fft_size/2))]) + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + + def get_freq(self): + return self.freq + + def set_freq(self, freq): + self.freq = freq + self.set_requested_freq_txt(self.freq) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 0) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 1) + + def get_freq_fine_range(self): + return self.freq_fine_range + + def set_freq_fine_range(self, freq_fine_range): + self.freq_fine_range = freq_fine_range + + def get_gain(self): + return self.gain + + def set_gain(self, gain): + self.gain = gain + + def get_lo_check_interval(self): + return self.lo_check_interval + + def set_lo_check_interval(self, lo_check_interval): + self.lo_check_interval = lo_check_interval + self.set_locked_probe_poll_rate(self.lo_check_interval * float(self.has_lo_locked)) + + def get_lo_offset(self): + return self.lo_offset + + def set_lo_offset(self, lo_offset): + self.lo_offset = lo_offset + self.set_lo_offset_txt(self.lo_offset) + self.set_tune_mode([1,0][self.lo_offset==0.0]) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 0) + self.src.set_center_freq(uhd.tune_request(self.freq, self.lo_offset), 1) + + def get_mag_alpha(self): + return self.mag_alpha + + def set_mag_alpha(self, mag_alpha): + self.mag_alpha = mag_alpha + self.probe_avg_mag.set_alpha(self.mag_alpha) + + def get_peak_hold(self): + return self.peak_hold + + def set_peak_hold(self, peak_hold): + self.peak_hold = peak_hold + self.set_fft_peak_hold(self.str_to_bool(self.peak_hold)) + + def get_pps(self): + return self.pps + + def set_pps(self, pps): + self.pps = pps + self._evalutate_update_time_source(**{'pps': self.pps}) + + def get_probe_interval(self): + return self.probe_interval + + def set_probe_interval(self, probe_interval): + self.probe_interval = probe_interval + self.set_fft_max_idx_poll_rate(self.probe_interval) + self.set_fft_max_lvl_poll_rate(self.probe_interval) + self.set_fft_max_lvl_2_poll_rate(self.probe_interval) + self.set_signal_probe_poll_rate(self.probe_interval) + + def get_rate(self): + return self.rate + + def set_rate(self, rate): + self.rate = rate + self.set_requested_sample_rate_base(self.rate) + + def get_ref(self): + return self.ref + + def set_ref(self, ref): + self.ref = ref + self._evalutate_update_clock_source(**{'ref': self.ref}) + + def get_ref_lvl(self): + return self.ref_lvl + + def set_ref_lvl(self, ref_lvl): + self.ref_lvl = ref_lvl + + def get_sensor_interval(self): + return self.sensor_interval + + def set_sensor_interval(self, sensor_interval): + self.sensor_interval = sensor_interval + self.set_daughterboard_sensor_names_poll_rate(self.sensor_interval) + self.set_motherboard_sensor_names_poll_rate(self.sensor_interval) + self.set_time_probe_poll_rate(self.sensor_interval) + + def get_show_stream_tags(self): + return self.show_stream_tags + + def set_show_stream_tags(self, show_stream_tags): + self.show_stream_tags = show_stream_tags + self.set_show_stream_tags_chk([False, True][self.show_stream_tags.lower() != 'false']) + + def get_spec(self): + return self.spec + + def set_spec(self, spec): + self.spec = spec + + def get_stream_args(self): + return self.stream_args + + def set_stream_args(self, stream_args): + self.stream_args = stream_args + + def get_window(self): + return self.window + + def set_window(self, window): + self.window = window + self.set_variable_static_fft_window_name(self.window_fn_name_map[self.window]) + self.set_window_fn(self.window_fn_map[self.window]) + + def get_wire_format(self): + return self.wire_format + + def set_wire_format(self, wire_format): + self.wire_format = wire_format + + def get_relative_freq(self): + return self.relative_freq + + def set_relative_freq(self, relative_freq): + self.relative_freq = relative_freq + self.set_fft_center_freq(self.actual_center_freq * self.relative_freq) + self._relative_freq_chooser.set_value(self.relative_freq) + + def get_gain_range(self): + return self.gain_range + + def set_gain_range(self, gain_range): + self.gain_range = gain_range + self._run_evaluators_later(('initial_gain', lambda: self._evalutate_initial_gain(**{'gain_range': self.gain_range}))) + + def get_fft_max_idx(self): + return self.fft_max_idx + + def set_fft_max_idx(self, fft_max_idx): + self.fft_max_idx = fft_max_idx + self.set_fft_max_idx_norm([self.fft_max_idx,self.fft_max_idx-self.fft_size][int(self.fft_max_idx>(self.fft_size/2))]) + + def get_actual_sample_rate(self): + return self.actual_sample_rate + + def set_actual_sample_rate(self, actual_sample_rate): + self.actual_sample_rate = actual_sample_rate + self.set_samp_rate(int(self.actual_sample_rate)) + self.set_variable_static_actual_sample_rate(self.actual_sample_rate) + + def get_actual_center_freq(self): + return self.actual_center_freq + + def set_actual_center_freq(self, actual_center_freq): + self.actual_center_freq = actual_center_freq + self.set_fft_center_freq(self.actual_center_freq * self.relative_freq) + + def get_str_to_bool(self): + return self.str_to_bool + + def set_str_to_bool(self, str_to_bool): + self.str_to_bool = str_to_bool + self.set_fft_averaging(self.str_to_bool(self.averaging)) + self.set_fft_peak_hold(self.str_to_bool(self.peak_hold)) + + def get_signal_probe(self): + return self.signal_probe + + def set_signal_probe(self, signal_probe): + self.signal_probe = signal_probe + self.set_signal_probe_log(math.log10([self.signal_probe,1.0][self.signal_probe==0.0])*10) + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + self.fft_sink.set_sample_rate(self.samp_rate) + self.fft_sink_two_1.set_sample_rate(self.samp_rate) + self.logpwrfft_x_0.set_sample_rate(self.samp_rate) + self.logpwrfft_x_0_0.set_sample_rate(self.samp_rate) + self.waterfall_sink.set_sample_rate(self.samp_rate) + self.waterfall_sink_0.set_sample_rate(self.samp_rate) + self.wxgui_scopesink2_0.set_sample_rate(self.samp_rate) + self.wxgui_scopesink2_0_0.set_sample_rate(self.samp_rate) + + def get_requested_freq_txt(self): + return self.requested_freq_txt + + def set_requested_freq_txt(self, requested_freq_txt): + self.requested_freq_txt = requested_freq_txt + self.set_requested_freq(self.requested_freq_txt + self.freq_fine) + self._requested_freq_txt_slider.set_value(self.requested_freq_txt) + self._requested_freq_txt_text_box.set_value(self.requested_freq_txt) + + def get_gain_default(self): + return self.gain_default + + def set_gain_default(self, gain_default): + self.gain_default = gain_default + self.set_selected_gain(self.gain_default) + + def get_freq_fine(self): + return self.freq_fine + + def set_freq_fine(self, freq_fine): + self.freq_fine = freq_fine + self._freq_fine_slider.set_value(self.freq_fine) + self._freq_fine_text_box.set_value(self.freq_fine) + self.set_requested_freq(self.requested_freq_txt + self.freq_fine) + + def get_fft_max_idx_norm(self): + return self.fft_max_idx_norm + + def set_fft_max_idx_norm(self, fft_max_idx_norm): + self.fft_max_idx_norm = fft_max_idx_norm + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + + def get_fft_center_freq(self): + return self.fft_center_freq + + def set_fft_center_freq(self, fft_center_freq): + self.fft_center_freq = fft_center_freq + self.set_max_bin_freq(self.fft_center_freq + (((1.0*self.fft_max_idx_norm) / self.fft_size) * self.samp_rate)) + self.fft_sink.set_baseband_freq(self.fft_center_freq) + self.fft_sink_two_1.set_baseband_freq(self.fft_center_freq) + self.waterfall_sink.set_baseband_freq(self.fft_center_freq) + self.waterfall_sink_0.set_baseband_freq(self.fft_center_freq) + + def get_antennas(self): + return self.antennas + + def set_antennas(self, antennas): + self.antennas = antennas + self.set_selected_antenna([ [self.antenna,self.antennas[0]][self.antenna not in self.antennas] ,self.antennas[0]][self.antenna=='']) + + def get_window_fn_name_map(self): + return self.window_fn_name_map + + def set_window_fn_name_map(self, window_fn_name_map): + self.window_fn_name_map = window_fn_name_map + self.set_variable_static_fft_window_name(self.window_fn_name_map[self.window]) + + def get_window_fn_map(self): + return self.window_fn_map + + def set_window_fn_map(self, window_fn_map): + self.window_fn_map = window_fn_map + self.set_window_fn(self.window_fn_map[self.window]) + + def get_usrp_info(self): + return self.usrp_info + + def set_usrp_info(self, usrp_info): + self.usrp_info = usrp_info + self.set_variable_static_usrp_info(self.usrp_info) + + def get_tune_result(self): + return self.tune_result + + def set_tune_result(self, tune_result): + self.tune_result = tune_result + self.set_variable_static_actual_dsp_freq(self.tune_result.actual_dsp_freq) + self.set_variable_static_rf_freq(self.tune_result.actual_rf_freq) + + def get_tune_mode(self): + return self.tune_mode + + def set_tune_mode(self, tune_mode): + self.tune_mode = tune_mode + self._tune_mode_chooser.set_value(self.tune_mode) + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + + def get_time_probe(self): + return self.time_probe + + def set_time_probe(self, time_probe): + self.time_probe = time_probe + self.set_variable_static_time_now(str( [time.ctime(self.time_probe.get_real_secs()), datetime.timedelta(seconds=self.time_probe.get_real_secs()), time.gmtime(self.time_probe.get_real_secs())] [self.relative_time])) + + def get_test(self): + return self.test + + def set_test(self, test): + self.test = test + self._run_evaluators_later(('any_test_1', lambda: self._evalutate_any_test_1(**{'test': self.test}))) + self._test_chooser.set_value(self.test) + + def get_subdev_spec(self): + return self.subdev_spec + + def set_subdev_spec(self, subdev_spec): + self.subdev_spec = subdev_spec + self.set_variable_static_subdev_spec(self.subdev_spec) + + def get_signal_probe_log(self): + return self.signal_probe_log + + def set_signal_probe_log(self, signal_probe_log): + self.signal_probe_log = signal_probe_log + self.set_variable_static_level_probe(self.signal_probe_log) + + def get_selected_gain(self): + return self.selected_gain + + def set_selected_gain(self, selected_gain): + self.selected_gain = selected_gain + self._selected_gain_slider.set_value(self.selected_gain) + self._selected_gain_text_box.set_value(self.selected_gain) + self.set_selected_gain_proxy(self.selected_gain) + + def get_selected_antenna(self): + return self.selected_antenna + + def set_selected_antenna(self, selected_antenna): + self.selected_antenna = selected_antenna + self._selected_antenna_chooser.set_value(self.selected_antenna) + self.set_selected_antenna_proxy(self.selected_antenna) + + def get_requested_sample_rate_base(self): + return self.requested_sample_rate_base + + def set_requested_sample_rate_base(self, requested_sample_rate_base): + self.requested_sample_rate_base = requested_sample_rate_base + self.set_requested_sample_rate(self.requested_sample_rate_base / (1.*self.decim)) + self._requested_sample_rate_base_text_box.set_value(self.requested_sample_rate_base) + + def get_requested_freq(self): + return self.requested_freq + + def set_requested_freq(self, requested_freq): + self.requested_freq = requested_freq + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + self.set_variable_static_requested_freq(self.requested_freq) + + def get_relative_time(self): + return self.relative_time + + def set_relative_time(self, relative_time): + self.relative_time = relative_time + self._relative_time_chooser.set_value(self.relative_time) + self.set_variable_static_time_now(str( [time.ctime(self.time_probe.get_real_secs()), datetime.timedelta(seconds=self.time_probe.get_real_secs()), time.gmtime(self.time_probe.get_real_secs())] [self.relative_time])) + + def get_motherboard_sensors(self): + return self.motherboard_sensors + + def set_motherboard_sensors(self, motherboard_sensors): + self.motherboard_sensors = motherboard_sensors + self.set_variable_static_text_0(self.motherboard_sensors) + + def get_max_bin_freq(self): + return self.max_bin_freq + + def set_max_bin_freq(self, max_bin_freq): + self.max_bin_freq = max_bin_freq + self.set_variable_static_max_bin_freq(self.max_bin_freq) + + def get_locked_probe(self): + return self.locked_probe + + def set_locked_probe(self, locked_probe): + self.locked_probe = locked_probe + self.set_static_locked('"' + str(self.locked_probe) + '"') + + def get_lo_offset_txt(self): + return self.lo_offset_txt + + def set_lo_offset_txt(self, lo_offset_txt): + self.lo_offset_txt = lo_offset_txt + self._lo_offset_txt_text_box.set_value(self.lo_offset_txt) + self.set_tune_obj([self.requested_freq, uhd.tune_request(self.requested_freq, self.lo_offset_txt), uhd.tune_request(self.requested_freq, dsp_freq=0, dsp_freq_policy=uhd.tune_request.POLICY_MANUAL)][self.tune_mode]) + + def get_fft_peak_hold(self): + return self.fft_peak_hold + + def set_fft_peak_hold(self, fft_peak_hold): + self.fft_peak_hold = fft_peak_hold + self._run_evaluators_later(('variable_any_code_fft_sink_peak_hold', lambda: self._evalutate_variable_any_code_fft_sink_peak_hold(**{'peak_hold': self.fft_peak_hold}))) + + def get_fft_max_lvl_2(self): + return self.fft_max_lvl_2 + + def set_fft_max_lvl_2(self, fft_max_lvl_2): + self.fft_max_lvl_2 = fft_max_lvl_2 + self.set_fft_max_lvl_value_2(self.fft_max_lvl_2) + + def get_fft_max_lvl(self): + return self.fft_max_lvl + + def set_fft_max_lvl(self, fft_max_lvl): + self.fft_max_lvl = fft_max_lvl + self.set_fft_max_lvl_value(self.fft_max_lvl) + self.set_variable_static_fft_max_lvl_log(self.fft_max_lvl) + + def get_fft_averaging(self): + return self.fft_averaging + + def set_fft_averaging(self, fft_averaging): + self.fft_averaging = fft_averaging + self._run_evaluators_later(('variable_any_code_fft_sink_ave', lambda: self._evalutate_variable_any_code_fft_sink_ave(**{'averaging': self.fft_averaging}))) + self._run_evaluators_later(('variable_any_code_waterfall_ave', lambda: self._evalutate_variable_any_code_waterfall_ave(**{'averaging': self.fft_averaging}))) + + def get_decim(self): + return self.decim + + def set_decim(self, decim): + self.decim = decim + self._decim_slider.set_value(self.decim) + self._decim_text_box.set_value(self.decim) + self.set_requested_sample_rate(self.requested_sample_rate_base / (1.*self.decim)) + + def get_daughterboard_sensors(self): + return self.daughterboard_sensors + + def set_daughterboard_sensors(self, daughterboard_sensors): + self.daughterboard_sensors = daughterboard_sensors + self.set_variable_static_text_0_0(self.daughterboard_sensors) + + def get_clicked_freq(self): + return self.clicked_freq + + def set_clicked_freq(self, clicked_freq): + self.clicked_freq = clicked_freq + self.set_clicked_freq_txt(self.clicked_freq) + + def get_window_fn(self): + return self.window_fn + + def set_window_fn(self, window_fn): + self.window_fn = window_fn + + def get_variable_static_usrp_info(self): + return self.variable_static_usrp_info + + def set_variable_static_usrp_info(self, variable_static_usrp_info): + self.variable_static_usrp_info = variable_static_usrp_info + self._variable_static_usrp_info_static_text.set_value(self.variable_static_usrp_info) + + def get_variable_static_time_now(self): + return self.variable_static_time_now + + def set_variable_static_time_now(self, variable_static_time_now): + self.variable_static_time_now = variable_static_time_now + self._variable_static_time_now_static_text.set_value(self.variable_static_time_now) + + def get_variable_static_text_0_0(self): + return self.variable_static_text_0_0 + + def set_variable_static_text_0_0(self, variable_static_text_0_0): + self.variable_static_text_0_0 = variable_static_text_0_0 + self._variable_static_text_0_0_static_text.set_value(self.variable_static_text_0_0) + + def get_variable_static_text_0(self): + return self.variable_static_text_0 + + def set_variable_static_text_0(self, variable_static_text_0): + self.variable_static_text_0 = variable_static_text_0 + self._variable_static_text_0_static_text.set_value(self.variable_static_text_0) + + def get_variable_static_subdev_spec(self): + return self.variable_static_subdev_spec + + def set_variable_static_subdev_spec(self, variable_static_subdev_spec): + self.variable_static_subdev_spec = variable_static_subdev_spec + self._variable_static_subdev_spec_static_text.set_value(self.variable_static_subdev_spec) + + def get_variable_static_rf_freq(self): + return self.variable_static_rf_freq + + def set_variable_static_rf_freq(self, variable_static_rf_freq): + self.variable_static_rf_freq = variable_static_rf_freq + self._variable_static_rf_freq_static_text.set_value(self.variable_static_rf_freq) + + def get_variable_static_requested_freq(self): + return self.variable_static_requested_freq + + def set_variable_static_requested_freq(self, variable_static_requested_freq): + self.variable_static_requested_freq = variable_static_requested_freq + self._variable_static_requested_freq_static_text.set_value(self.variable_static_requested_freq) + + def get_variable_static_max_bin_freq(self): + return self.variable_static_max_bin_freq + + def set_variable_static_max_bin_freq(self, variable_static_max_bin_freq): + self.variable_static_max_bin_freq = variable_static_max_bin_freq + self._variable_static_max_bin_freq_static_text.set_value(self.variable_static_max_bin_freq) + + def get_variable_static_level_probe(self): + return self.variable_static_level_probe + + def set_variable_static_level_probe(self, variable_static_level_probe): + self.variable_static_level_probe = variable_static_level_probe + self._variable_static_level_probe_static_text.set_value(self.variable_static_level_probe) + + def get_variable_static_fft_window_name(self): + return self.variable_static_fft_window_name + + def set_variable_static_fft_window_name(self, variable_static_fft_window_name): + self.variable_static_fft_window_name = variable_static_fft_window_name + self._variable_static_fft_window_name_static_text.set_value(self.variable_static_fft_window_name) + + def get_variable_static_fft_max_lvl_log(self): + return self.variable_static_fft_max_lvl_log + + def set_variable_static_fft_max_lvl_log(self, variable_static_fft_max_lvl_log): + self.variable_static_fft_max_lvl_log = variable_static_fft_max_lvl_log + self._variable_static_fft_max_lvl_log_static_text.set_value(self.variable_static_fft_max_lvl_log) + + def get_variable_static_actual_sample_rate(self): + return self.variable_static_actual_sample_rate + + def set_variable_static_actual_sample_rate(self, variable_static_actual_sample_rate): + self.variable_static_actual_sample_rate = variable_static_actual_sample_rate + self._variable_static_actual_sample_rate_static_text.set_value(self.variable_static_actual_sample_rate) + + def get_variable_static_actual_dsp_freq(self): + return self.variable_static_actual_dsp_freq + + def set_variable_static_actual_dsp_freq(self, variable_static_actual_dsp_freq): + self.variable_static_actual_dsp_freq = variable_static_actual_dsp_freq + self._variable_static_actual_dsp_freq_static_text.set_value(self.variable_static_actual_dsp_freq) + + def get_variable_any_code_waterfall_ave(self): + return self.variable_any_code_waterfall_ave + + def set_variable_any_code_waterfall_ave(self, variable_any_code_waterfall_ave): + self.variable_any_code_waterfall_ave = variable_any_code_waterfall_ave + + def get_variable_any_code_iq_correction(self): + return self.variable_any_code_iq_correction + + def set_variable_any_code_iq_correction(self, variable_any_code_iq_correction): + self.variable_any_code_iq_correction = variable_any_code_iq_correction + + def get_variable_any_code_fft_sink_peak_hold(self): + return self.variable_any_code_fft_sink_peak_hold + + def set_variable_any_code_fft_sink_peak_hold(self, variable_any_code_fft_sink_peak_hold): + self.variable_any_code_fft_sink_peak_hold = variable_any_code_fft_sink_peak_hold + + def get_variable_any_code_fft_sink_ave(self): + return self.variable_any_code_fft_sink_ave + + def set_variable_any_code_fft_sink_ave(self, variable_any_code_fft_sink_ave): + self.variable_any_code_fft_sink_ave = variable_any_code_fft_sink_ave + + def get_variable_any_code_auto_dc_offset_removal(self): + return self.variable_any_code_auto_dc_offset_removal + + def set_variable_any_code_auto_dc_offset_removal(self, variable_any_code_auto_dc_offset_removal): + self.variable_any_code_auto_dc_offset_removal = variable_any_code_auto_dc_offset_removal + + def get_update_time_source(self): + return self.update_time_source + + def set_update_time_source(self, update_time_source): + self.update_time_source = update_time_source + + def get_update_clock_source(self): + return self.update_clock_source + + def set_update_clock_source(self, update_clock_source): + self.update_clock_source = update_clock_source + + def get_tune_obj(self): + return self.tune_obj + + def set_tune_obj(self, tune_obj): + self.tune_obj = tune_obj + self._run_evaluators_later(('actual_center_freq', lambda: self._evalutate_actual_center_freq(**{'tune_obj': self.tune_obj}))) + self._run_evaluators_later(('tune_result', lambda: self._evalutate_tune_result(**{'tune_obj': self.tune_obj}))) + + def get_static_locked(self): + return self.static_locked + + def set_static_locked(self, static_locked): + self.static_locked = static_locked + self._static_locked_static_text.set_value(self.static_locked) + + def get_show_stream_tags_chk(self): + return self.show_stream_tags_chk + + def set_show_stream_tags_chk(self, show_stream_tags_chk): + self.show_stream_tags_chk = show_stream_tags_chk + self._show_stream_tags_chk_check_box.set_value(self.show_stream_tags_chk) + self.blocks_tag_debug_0.set_display(self.show_stream_tags_chk) + + def get_show_max_lvl(self): + return self.show_max_lvl + + def set_show_max_lvl(self, show_max_lvl): + self.show_max_lvl = show_max_lvl + self._show_max_lvl_check_box.set_value(self.show_max_lvl) + + def get_show_max_freq(self): + return self.show_max_freq + + def set_show_max_freq(self, show_max_freq): + self.show_max_freq = show_max_freq + self._show_max_freq_check_box.set_value(self.show_max_freq) + + def get_selected_gain_proxy(self): + return self.selected_gain_proxy + + def set_selected_gain_proxy(self, selected_gain_proxy): + self.selected_gain_proxy = selected_gain_proxy + self.src.set_gain(self.selected_gain_proxy, 0) + self.src.set_gain(self.selected_gain_proxy, 1) + + def get_selected_antenna_proxy(self): + return self.selected_antenna_proxy + + def set_selected_antenna_proxy(self, selected_antenna_proxy): + self.selected_antenna_proxy = selected_antenna_proxy + self.src.set_antenna(self.selected_antenna_proxy, 0) + self.src.set_antenna(self.selected_antenna_proxy, 1) + + def get_scope_mode_2(self): + return self.scope_mode_2 + + def set_scope_mode_2(self, scope_mode_2): + self.scope_mode_2 = scope_mode_2 + self._scope_mode_2_chooser.set_value(self.scope_mode_2) + self.blks2_selector_0_0_0.set_output_index(int(self.scope_mode_2)) + self.blks2_selector_0_1.set_input_index(int(self.scope_mode_2)) + + def get_scope_mode(self): + return self.scope_mode + + def set_scope_mode(self, scope_mode): + self.scope_mode = scope_mode + self._scope_mode_chooser.set_value(self.scope_mode) + self.blks2_selector_0.set_input_index(int(self.scope_mode)) + self.blks2_selector_0_0.set_output_index(int(self.scope_mode)) + + def get_requested_sample_rate(self): + return self.requested_sample_rate + + def set_requested_sample_rate(self, requested_sample_rate): + self.requested_sample_rate = requested_sample_rate + self._run_evaluators_later(('actual_sample_rate', lambda: self._evalutate_actual_sample_rate(**{'requested_sample_rate': self.requested_sample_rate}))) + self.src.set_samp_rate(self.requested_sample_rate) + + def get_motherboard_sensor_names(self): + return self.motherboard_sensor_names + + def set_motherboard_sensor_names(self, motherboard_sensor_names): + self.motherboard_sensor_names = motherboard_sensor_names + self._run_evaluators_later(('motherboard_sensors', lambda: self._evalutate_motherboard_sensors(**{'motherboard_sensor_names': self.motherboard_sensor_names}))) + + def get_max_decim(self): + return self.max_decim + + def set_max_decim(self, max_decim): + self.max_decim = max_decim + + def get_initial_gain(self): + return self.initial_gain + + def set_initial_gain(self, initial_gain): + self.initial_gain = initial_gain + self._run_evaluators_later(('gain_default', lambda: self._evalutate_gain_default(**{'initial_gain': self.initial_gain}))) + + def get_has_lo_locked(self): + return self.has_lo_locked + + def set_has_lo_locked(self, has_lo_locked): + self.has_lo_locked = has_lo_locked + self.set_locked_probe_poll_rate(self.lo_check_interval * float(self.has_lo_locked)) + + def get_freq_range(self): + return self.freq_range + + def set_freq_range(self, freq_range): + self.freq_range = freq_range + + def get_fix_invalid_freq(self): + return self.fix_invalid_freq + + def set_fix_invalid_freq(self, fix_invalid_freq): + self.fix_invalid_freq = fix_invalid_freq + + def get_fft_max_lvl_value_2(self): + return self.fft_max_lvl_value_2 + + def set_fft_max_lvl_value_2(self, fft_max_lvl_value_2): + self.fft_max_lvl_value_2 = fft_max_lvl_value_2 + + def get_fft_max_lvl_value(self): + return self.fft_max_lvl_value + + def set_fft_max_lvl_value(self, fft_max_lvl_value): + self.fft_max_lvl_value = fft_max_lvl_value + + def get_fft_ave_probe(self): + return self.fft_ave_probe + + def set_fft_ave_probe(self, fft_ave_probe): + self.fft_ave_probe = fft_ave_probe + self.logpwrfft_x_0.set_avg_alpha(self.fft_ave_probe) + self.logpwrfft_x_0_0.set_avg_alpha(self.fft_ave_probe) + + def get_daughterboard_sensor_names(self): + return self.daughterboard_sensor_names + + def set_daughterboard_sensor_names(self, daughterboard_sensor_names): + self.daughterboard_sensor_names = daughterboard_sensor_names + self._run_evaluators_later(('daughterboard_sensors', lambda: self._evalutate_daughterboard_sensors(**{'daughterboard_sensor_names': self.daughterboard_sensor_names}))) + + def get_clicked_freq_txt(self): + return self.clicked_freq_txt + + def set_clicked_freq_txt(self, clicked_freq_txt): + self.clicked_freq_txt = clicked_freq_txt + self._clicked_freq_txt_text_box.set_value(self.clicked_freq_txt) + + def get_auto_iq_correction(self): + return self.auto_iq_correction + + def set_auto_iq_correction(self, auto_iq_correction): + self.auto_iq_correction = auto_iq_correction + self._auto_iq_correction_check_box.set_value(self.auto_iq_correction) + self._run_evaluators_later(('variable_any_code_iq_correction', lambda: self._evalutate_variable_any_code_iq_correction(**{'auto_iq_correction': self.auto_iq_correction}))) + + def get_auto_dc_offset_removal(self): + return self.auto_dc_offset_removal + + def set_auto_dc_offset_removal(self, auto_dc_offset_removal): + self.auto_dc_offset_removal = auto_dc_offset_removal + self._auto_dc_offset_removal_check_box.set_value(self.auto_dc_offset_removal) + self._run_evaluators_later(('variable_any_code_auto_dc_offset_removal', lambda: self._evalutate_variable_any_code_auto_dc_offset_removal(**{'auto_dc_offset_removal': self.auto_dc_offset_removal}))) + + def get_any_test_1(self): + return self.any_test_1 + + def set_any_test_1(self, any_test_1): + self.any_test_1 = any_test_1 + + +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + parser.add_option("-A", "--antenna", dest="antenna", type="string", default="", + help="Set Antenna (blank for default) [default=%default]") + parser.add_option("-a", "--args", dest="args", type="string", default="", + help="Set UHD device args [default=%default]") + parser.add_option("", "--ave", dest="ave", type="eng_float", default=eng_notation.num_to_str(1*0 + 0.5), + help="Set Average FFT [default=%default]") + parser.add_option("", "--averaging", dest="averaging", type="string", default="True", + help="Set Enable FFT averaging [default=%default]") + parser.add_option("", "--bw", dest="bw", type="eng_float", default=eng_notation.num_to_str(0), + help="Set Daughterboard bandwidth (where appropriate) [default=%default]") + parser.add_option("", "--dyn-rng", dest="dyn_rng", type="eng_float", default=eng_notation.num_to_str(130), + help="Set Dynamic Range [default=%default]") + parser.add_option("", "--fft-rate", dest="fft_rate", type="intx", default=15, + help="Set FFT Rate [default=%default]") + parser.add_option("", "--fft-ref-scale", dest="fft_ref_scale", type="eng_float", default=eng_notation.num_to_str(2.0), + help="Set FFT Ref Scale (p2p) [default=%default]") + parser.add_option("", "--fft-size", dest="fft_size", type="intx", default=1024, + help="Set FFT Size [default=%default]") + parser.add_option("-f", "--freq", dest="freq", type="eng_float", default=eng_notation.num_to_str(0 + 100e6), + help="Set Frequency [default=%default]") + parser.add_option("", "--freq-fine-range", dest="freq_fine_range", type="eng_float", default=eng_notation.num_to_str(2e6), + help="Set Fine frequency slider range [default=%default]") + parser.add_option("-g", "--gain", dest="gain", type="eng_float", default=eng_notation.num_to_str(float("-inf")), + help="Set Gain (default '-inf' selects relative gain of 25%) [default=%default]") + parser.add_option("", "--lo-check-interval", dest="lo_check_interval", type="eng_float", default=eng_notation.num_to_str(5), + help="Set LO lock check frequency (Hz) [default=%default]") + parser.add_option("", "--lo-offset", dest="lo_offset", type="eng_float", default=eng_notation.num_to_str(0), + help="Set LO offset (selects LO offset tuning mode) [default=%default]") + parser.add_option("", "--mag-alpha", dest="mag_alpha", type="eng_float", default=eng_notation.num_to_str(1e-3), + help="Set Signal magnitude probe alpha [default=%default]") + parser.add_option("", "--peak-hold", dest="peak_hold", type="string", default="False", + help="Set FFT peak hold [default=%default]") + parser.add_option("", "--pps", dest="pps", type="string", default='', + help="Set Time source (none, internal, external, mimo, gpsdo). <empty> leaves it at the default. [default=%default]") + parser.add_option("", "--probe-interval", dest="probe_interval", type="eng_float", default=eng_notation.num_to_str(3), + help="Set Signal probe frequency (Hz) [default=%default]") + parser.add_option("-s", "--rate", dest="rate", type="eng_float", default=eng_notation.num_to_str(1e6), + help="Set Sample Rate [default=%default]") + parser.add_option("", "--ref", dest="ref", type="string", default='', + help="Set Clock source (internal, external, mimo, gpsdo). <empty> leaves it at the default. [default=%default]") + parser.add_option("", "--ref-lvl", dest="ref_lvl", type="eng_float", default=eng_notation.num_to_str(0), + help="Set Reference Level [default=%default]") + parser.add_option("", "--sensor-interval", dest="sensor_interval", type="eng_float", default=eng_notation.num_to_str(2), + help="Set Sensor update frequency (Hz) [default=%default]") + parser.add_option("", "--show-stream-tags", dest="show_stream_tags", type="string", default="False", + help="Set Print stream tags [default=%default]") + parser.add_option("", "--spec", dest="spec", type="string", default='', + help="Set Sub-device specification (where appropriate) [default=%default]") + parser.add_option("", "--stream-args", dest="stream_args", type="string", default="", + help="Set Stream arguments (e.g. scalar=1024) [default=%default]") + parser.add_option("", "--window", dest="window", type="string", default="auto", + help="Set Window (bh: Blackman-Harris, ham: Hamming, han: Hanning, rect: Rectangular, flat: Flattop) [default=%default]") + parser.add_option("", "--wire-format", dest="wire_format", type="string", default="", + help="Set Wire format (e.g. sc16, sc8) [blank = automatic] [default=%default]") + (options, args) = parser.parse_args() + if gr.enable_realtime_scheduling() != gr.RT_OK: + print "Error: failed to enable realtime scheduling." + tb = mega_fft_2ch(antenna=options.antenna, args=options.args, ave=options.ave, averaging=options.averaging, bw=options.bw, dyn_rng=options.dyn_rng, fft_rate=options.fft_rate, fft_ref_scale=options.fft_ref_scale, fft_size=options.fft_size, freq=options.freq, freq_fine_range=options.freq_fine_range, gain=options.gain, lo_check_interval=options.lo_check_interval, lo_offset=options.lo_offset, mag_alpha=options.mag_alpha, peak_hold=options.peak_hold, pps=options.pps, probe_interval=options.probe_interval, rate=options.rate, ref=options.ref, ref_lvl=options.ref_lvl, sensor_interval=options.sensor_interval, show_stream_tags=options.show_stream_tags, spec=options.spec, stream_args=options.stream_args, window=options.window, wire_format=options.wire_format) + tb.Start(True) + tb.Wait() diff --git a/tools/nirio_programmer/.gitignore b/tools/nirio_programmer/.gitignore new file mode 100644 index 000000000..567609b12 --- /dev/null +++ b/tools/nirio_programmer/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/tools/nirio_programmer/CMakeLists.txt b/tools/nirio_programmer/CMakeLists.txt new file mode 100644 index 000000000..bde0ea574 --- /dev/null +++ b/tools/nirio_programmer/CMakeLists.txt @@ -0,0 +1,83 @@ +# +# Copyright 2014-2015 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +cmake_minimum_required(VERSION 2.8) + +### Set up build environment ################################################## +# Choose a static or shared-library build (shared is default, and static will +# probably need some special care!) +# Set this to ON in order to link a static build of UHD: +option(UHD_USE_STATIC_LIBS OFF) + +# This example also requires Boost: +set(BOOST_REQUIRED_COMPONENTS + program_options + system + thread + regex +) +if(MSVC) + set(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking") + if(BOOST_ALL_DYN_LINK) + add_definitions(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc + else(BOOST_ALL_DYN_LINK) + set(BOOST_REQUIRED_COMPONENTS) #empty components list for static link + endif(BOOST_ALL_DYN_LINK) +endif(MSVC) +find_package(Boost "1.46" REQUIRED ${BOOST_REQUIRED_COMPONENTS}) + +# To add UHD as a dependency to this project, add a line such as this: +find_package(UHD "3.8.0" REQUIRED) +# The version in ^^^^^ here is a minimum version. +# To specify an exact version: +#find_package(UHD 3.8.1 EXACT REQUIRED) + +### Configure Compiler ######################################################## +include_directories( + "${CMAKE_CURRENT_SOURCE_DIR}/../../host/include" + ${Boost_INCLUDE_DIRS} + ${UHD_INCLUDE_DIRS} +) +link_directories(${Boost_LIBRARY_DIRS}) + +### Make the executable ####################################################### +add_executable(nirio_programmer nirio_programmer.cpp) + +SET(CMAKE_BUILD_TYPE "Release") + +# Shared library case: All we need to do is link against the library, and +# anything else we need (in this case, some Boost libraries): +if(NOT UHD_USE_STATIC_LIBS) + message(STATUS "Linking against shared UHD library.") + target_link_libraries(nirio_programmer ${UHD_LIBRARIES} ${Boost_LIBRARIES}) +# Shared library case: All we need to do is link against the library, and +# anything else we need (in this case, some Boost libraries): +else(NOT UHD_USE_STATIC_LIBS) + message(STATUS "Linking against static UHD library.") + target_link_libraries(nirio_programmer + # We could use ${UHD_LIBRARIES}, but linking requires some extra flags, + # so we use this convenience variable provided to us + ${UHD_STATIC_LIB_LINK_FLAG} + # Also, when linking statically, we need to pull in all the deps for + # UHD as well, because the dependencies don't get resolved automatically + ${UHD_STATIC_LIB_DEPS} + ) +endif(NOT UHD_USE_STATIC_LIBS) + +### Once it's built... ######################################################## +# Here, you would have commands to install your program. +# We will skip these in this example. diff --git a/host/utils/nirio_programmer.cpp b/tools/nirio_programmer/nirio_programmer.cpp index c8c5e72d3..9bb83e9a4 100644 --- a/host/utils/nirio_programmer.cpp +++ b/tools/nirio_programmer/nirio_programmer.cpp @@ -1,3 +1,19 @@ +// +// Copyright 2014 Ettus Research LLC +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// #include <uhd/transport/nirio/niusrprio_session.h> #include <uhd/transport/nirio/niriok_proxy.h> diff --git a/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_README.md b/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_README.md new file mode 100644 index 000000000..1425a4d96 --- /dev/null +++ b/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_README.md @@ -0,0 +1,17 @@ +UHD TX/RX DEBUG PRINTS +====================== + +A tool for extensive debugging with UHD. + +Install +------- +Activate it by ticking `UHD_TXRX_DEBUG_PRINTS` in cmake-gui for your UHD installation. Then recompile and reinstall UHD. + +Use +--- +Run your application and pipe stderr to a file. +this is mostly done by <br> +`app_call 2> dbg_print_file.txt`<br> +After finishing the application offline processing of the gathered data is done with a python script called<br> +`uhd_txrx_debug_prints_graph.py`<br> +There are a lot of functions that help to preprocess your data and that describe the actual meaning of all the data points. in the end though, it comes down to the users needs what he wants to plot and see.
\ No newline at end of file diff --git a/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_graph.py b/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_graph.py new file mode 100755 index 000000000..b0f6681f6 --- /dev/null +++ b/tools/uhd_txrx_debug_prints/uhd_txrx_debug_prints_graph.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2013-2014 Ettus Research LLC +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +""" +Plots the output of the streamers that is produced when DEBUG_TXRX +is enabled. +""" + +import argparse +import matplotlib.pyplot as plt +import numpy as np + +# This is a top level function to load a debug file. It will return a list of lists with all the data. +def get_data(filename): + res = [] + with open(filename) as f: + for line in f.readlines(): + line = line.rstrip('\n') + s = line.split(',') + res.append(s) + res = fix_known_data_corruption_patterns(res) + return res + +# Sometimes 'O' etc. get printed to stderr. This disturbs further processing. Thus, known pattern will be removed here. +def fix_known_data_corruption_patterns(data): + # Some lines might be corrupted in the way that e.g. UHD prints sth else in the same line. + # O, D, Press Enter... are known patterns. These should be fixed. + counts = {'O': 0, 'D': 0, 'exit_msg': 0} + for i in range(len(data)): + if data[i][0].startswith('O'): + counts['O'] += 1 + data[i][0] = data[i][0].replace('O', '') + if data[i][0].startswith('D'): + counts['D'] += 1 + data[i][0] = data[i][0].replace('D', '') + if data[i][0].startswith('Press Enter to quit: '): + counts['exit_msg'] += 1 + data[i][0] = data[i][0].replace('Press Enter to quit: ', '') + print counts + return data + +# Extract lines with a certain prefix. +def extract_lines(prefix, data): + res = [] + for line in data: + if line[0] == prefix: + res.append(line[1:]) + return res + +# boolean values are stored as true/false. Convert them to real booleans again. +def convert_to_bool(data): + res = [] + for d in data: + res.append(d == "true") + return res + +# The different outputs have different structures. With this function you can conert them back to their actual type. +def convert_data_set(data, reqtype): + zdata = zip(*data) + res = [] + if len(zdata) == 0: + return res + for i in range(len(reqtype)): + if reqtype[i] == np.bool: + res.append(np.asarray(convert_to_bool(zdata[i]), dtype=np.bool)) + else: + res.append(np.asarray(zdata[i], dtype=reqtype[i])) + return res + +# Wrapper for super_send_packet_handler data to be used with convert_data_set +def convert_super_send_packet_handler_data(data): + # wallclock, timeout, avail_samps, sent_samps, sob, eob, has_time_spec, time_spec (ticks) + reqtype = [np.uint64, np.float, np.int, np.int, np.bool, np.bool, np.bool, np.uint64] + cdata = convert_data_set(data, reqtype) + return cdata + +# same o' same o' +def convert_super_recv_packet_handler_data(data): + # wallclock, timeout, requested_samps, received_samps, one_packet, error_code, sob, eob, more_fragments, fragment_offset, has_timespec, time_spec + reqtype = [np.uint64, np.float, np.int, np.int, np.bool, np.int, np.bool, np.bool, np.bool, np.int, np.bool, np.uint64] + cdata = convert_data_set(data, reqtype) + return cdata + +def extract_super_recv_packet_handler_data(data): + pref1 = "super_recv_packet_handler" + pref2 = "recv" + super_recv = extract_lines(pref1, data) + recv = extract_lines(pref2, super_recv) + recv = convert_super_recv_packet_handler_data(recv) + return recv + +# Sometimes TX or RX is interrupted by system jiffies. Those are found by this function. +def find_jiffy(data, thr): + res = [] + last = data[0] + for i in range(len(data)): + if data[i] - last > thr: + res.append([last, data[i]]) + last = data[i] + return res + + +# Get difference between tx and rx wallclock +def get_diff(tx, rx): + print "get diff, len(rx) = ", len(rx) + diff = [0] * len(rx[0]) + for i in range(len(diff)): + r = rx[3][i] + idx = rx[0][i] - 1 # call count starts at 1. idx is 0 based. + t = tx[3][idx] + diff[i] = t - r + return diff + + +def bps(samps, time): + bp = [] + last = time[0]-1000 + for i in range(len(samps)): + td = time[i] - last + last = time[i] + td = td /1e6 + bp.append(samps[i] * 4 / td) + return bp + + +# same as the other wrappers this time for libusb1 +def extract_libusb(trx, data): + pr1 = "libusb1_zero_copy" + pr2 = "libusb_async_cb" + ldata = extract_lines(pr1, data) + edata = extract_lines(pr2, ldata) + d = extract_lines(trx, edata) + # buff_num, actual_length, status, end_time, start_time + reqtype = [np.int, np.int, np.int, np.uint64, np.uint64] + return convert_data_set(d, reqtype) + + +# Extract data for stream buffers. Typically there are 16 TX and 16 RX buffers. And there numbers are static. Though the number of buffers might be changed and the constant parameters must be adjusted in this case. +def extract_txrx(data): + tx = [[], [], [], [], []] + rx = [[], [], [], [], []] + for i in range(len(data[0])): + print data[0][i] + if data[0][i] > 31 and data[0][i] < 48: + rx[0].append(data[0][i]) + rx[1].append(data[1][i]) + rx[2].append(data[2][i]) + rx[3].append(data[3][i]) + rx[4].append(data[4][i]) + #print "tx\t", data[0][i], "\t", data[3][i], "\t", data[4][i] + if data[0][i] > 47 and data[0][i] < 64: + tx[0].append(data[0][i]) + tx[1].append(data[1][i]) + tx[2].append(data[2][i]) + tx[3].append(data[3][i]) + tx[4].append(data[4][i]) + #print "rx\t", data[0][i], "\t", data[3][i], "\t", data[4][i] + return [tx, rx] + +# Calculate momentary throughput +def throughput(data): + start = data[2][0] + total = data[1][-1] - data[2][0] + print total + thr = np.zeros(total) + + for i in range(len(data[0])): + s = data[2][i] - start + f = data[1][i] - start + ticks = data[1][i] - data[2][i] + pertick = 1. * data[0][i] / ticks + vals = [pertick] * ticks + thr[s:f] = np.add(thr[s:f], vals) + #print pertick + print np.shape(thr) + return thr + + +# Calculate a moving average +def ma(data, wl): + ap = np.zeros(wl) + data = np.concatenate((ap, data, ap)) + print np.shape(data) + res = np.zeros(len(data)-wl) + for i in range(len(data)-wl): + av = np.sum(data[i:i+wl]) / wl + res[i] = av + print i, "\t", av + return res + + +def get_x_axis(stamps): + scale = 10e-6 + return np.multiply(stamps, scale) + + +# plot status codes. +def plot_status_codes_over_time(data, fignum): + print "printing status numbers over time" + + # extract and convert the data + recv = extract_super_recv_packet_handler_data(data) + libusb_rx = extract_libusb("rx", data) + libusb_tx = extract_libusb("tx", data) + + # Plot all data + plt.figure(fignum) # Make sure these plots are printed to a new figure. + #plt.plot(get_x_axis(libusb_rx[3]), libusb_rx[2], marker='x', label='RX') + #plt.plot(get_x_axis(libusb_tx[3]), libusb_tx[2], marker='x', label='TX') + + pos = 5 + recv_error_codes = recv[pos] + plt.plot(get_x_axis(recv[0]), recv_error_codes, marker='x', label='recv') + plt.title("Status codes over timestamps") + plt.ylabel("status codes") + plt.xlabel("timestamps") + plt.grid() + plt.legend() + + for i in range(len(recv[0])): + if recv[pos][i] == 8: + xaxis = get_x_axis([recv[0][i]]) + plt.axvline(xaxis, color='b') + + + # Get some statistics and print them too + codes = [] + code_dict = {} + for i in range(len(recv_error_codes)): + if recv_error_codes[i] != 0: + code = rx_metadata_error_codes[recv_error_codes[i]] + pair = [i, code] + codes.append(pair) + if not code_dict.has_key(code): + code_dict[code] = 0 + code_dict[code] += 1 + print codes + print code_dict + + +# plot rtt times as peaks. That's the fast and easy way. +def plot_rtt_times(data, fignum): + print "plot RTT times" + rx = extract_libusb("rx", data) + tx = extract_libusb("tx", data) + + scale = 10e-6 + rx_diff = np.multiply(np.subtract(rx[3], rx[4]), scale) + tx_diff = np.multiply(np.subtract(tx[3], tx[4]), scale) + + plt.figure(fignum) + plt.plot(get_x_axis(rx[3]), rx_diff, marker='x', ls='', label="rx RTT") + plt.plot(get_x_axis(tx[3]), tx_diff, marker='x', ls='', label="tx RTT") + plt.title("Round trip times") + plt.ylabel("RTT (us)") + plt.grid() + plt.legend() + + +# plot RTT as actual lines as long as buffers are on the fly. +# This can take a long time if the function has to print a lot of small lines. Careful with this! +def plot_rtt_lines(data, fignum): + print "plot RTT lines" + rx = extract_libusb("rx", data) + #tx = extract_libusb("tx", data) + + if len(data) == 0 or len(rx) == 0: + return + + plt.figure(fignum) + for i in range(len(rx[0])): + if rx[0][i] > -1: + start = rx[4][i] + stop = rx[3][i] + status = rx[2][i] + + val = rx[0][i]#(stop - start) * scale + if not status == 0: + if status == 2: + print "status = ", status + val +=0.5 + xaxis = get_x_axis([start, stop]) + plt.plot(xaxis, [val, val], marker='x') + plt.ylabel('buffer number') + + # Careful with these lines here. + # Basically they should always have these values to separate CTRL, TX, RX buffer number blocks. + # But these values can be adjusted. Thus, it requires you to adjust these lines + plt.axhline(15.5) + plt.axhline(31.5) + plt.axhline(47.5) + plt.grid() + + +# only plot on-the-fly buffers. +def plot_buff_otf(trx, nrange, data): + d = extract_libusb(trx, data) + res = [[], []] + num = 0 + for i in range(len(d[0])): + if d[0][i] in nrange: + res + + +# If there are still unknown lines after cleanup, they can be caught and printed here. +# This way you can check what got caught but shouldn't have been caught. +def get_unknown_lines(data): + # These are the 3 known starting lines. More might be added in the future. + known = ['super_recv_packet_handler', 'super_send_packet_handler', 'libusb1_zero_copy'] + res = [] + for i in range(len(data)): + if not data[i][0] in known: + print data[i] + res.append(data[i]) + return res + +# LUT for all the return codes +rx_metadata_error_codes = {0x0: "NONE", 0x1: "TIMEOUT", 0x2: "LATE_COMMAND", 0x4: "BROKEN_CHAIN", 0x8: "OVERFLOW", + 0xc: "ALIGNMENT", 0xf: "BAD_PACKET"} + + +def parse_args(): + """ Parse args, yo """ + parser = argparse.ArgumentParser(description='Plot tool for debug prints.') + parser.add_argument('-f', '--filename', default=None, help='Debug output file') + return parser.parse_args() + +# Be really careful with the input files. They get pretty huge in a small time. +# Doing some of the plotting can eat up a lot of time then. +# Although this file contains a lot of functions by now, it is still left to the user to use everythin correctly. +def main(): + args = parse_args() + filename = args.filename + print "get data from: ", filename + #pref1 = "super_recv_packet_handler" + #pref2 = "recv" + + # Here you get the data from a file + data = get_data(filename) + #print "data len: ", len(data) + + # extract lines with unknown content. + unknown = get_unknown_lines(data) + + # plot status codes and RTT lines. + plot_status_codes_over_time(data, 0) + plot_rtt_lines(data, 0) + #plot_rtt_times(data, 0) + + #[tx, rx] = extract_txrx(data) + #print "txlen\t", len(tx[0]) + #print "rxlen\t", len(rx[0]) + + + #print "plot data" + #plt.title(filename) + #plt.plot(tx_data[0], tx_data[7], 'r', marker='o', label='TX') + #plt.plot(rx_data[0], rx_data[11], 'g', marker='x', label='RX') + #for j in jiffies: + # plt.axvline(x=j[0], color='r') + # plt.axvline(x=j[1], color='g') + # + #plt.xlabel('wallclock (us)') + #plt.ylabel('timestamp (ticks)') + #plt.legend() + + #lencal = 200000 + + # calculate and plot rx throughput + #rxthr = throughput([rx[1], rx[3], rx[4]]) + #plt.plot(rxthr[0:lencal]) + #rxav = ma(rxthr[0:lencal], 2000) + #plt.plot(rxav) + + # calculate and plot tx throughput + #txthr = throughput([tx[1], tx[3], tx[4]]) + #plt.plot(txthr[0:lencal]) + #txav = ma(txthr[0:lencal], 20) + #plt.plot(txav) + + + #bp = bps(data[1], data[2]) + #print np.sum(bp) + #ave = np.sum(bp)/len(bp) + #print ave + #plt.plot(tx_data[0], tx_data[2], 'r') + #plt.plot(rx_data[0], rx_data[2], 'b') + #plt.plot(bp, 'g', marker='+') + #plt.plot(np.multiply(data[1], 1e6), 'r', marker='o') + #plt.plot(np.multiply(data[3],1e2), 'b', marker='x') + #plt.plot(ave) + + # in the end, put a grid on the graph and show it. + plt.grid() + plt.show() + +if __name__ == '__main__': + print "[WARNING] This tool is in alpha status. Only use if you know what you're doing!" + main() + |