// --------------------------------------------------------------------------------------
// Module Name:		LMS Synthesizer Device Control Linux Library
// JA 04/18/2020	Initial Verison oF Updated LMS Synthesizer library using libusb1.0
// RD 06/4/2024		Fixed LMS-183DX endpoint issue, added LMS-183CX support, improved SetFastPulseOutput
//					corrected some legacy power ranges
// --------------------------------------------------------------------------------------
#include <libusb-1.0/libusb.h>
#include <linux/hid.h>  
#include <stdbool.h>        
#include <stdint.h>         
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "lmsusb.h"

/// ---------- Macros ----------------

#define DEBUG_OUT 0 		/* set this to 1 in order to see debugging output, 2 for a ton of output, or 3 for many tons */ 
#define FALSE               0
#define TRUE                !FALSE
#define PACKET_CTRL_LEN     8
#define PACKET_INT_LEN      8
#define INTERFACE           0
#define ENDPOINT_INT_IN1	0x81
#define ENDPOINT_INT_IN2    0x82
#define TIMEOUT             500

#define LIBVER              "1.05"
#define LIBVER_NUM			105

// Thread State Macros
#define THREAD_IDLE         0
#define THREAD_START        1
#define THREAD_WRITE        2		// unused
#define THREAD_EXIT         3
#define THREAD_DEAD         4
#define THREAD_ERROR        -1

// --------------------- other device specific equates ------------------------
#define HW_MAXP_10      40	// MaxPower is the output of the device in db -- +10 in this case
							// NB -- the value used in the SetPower function is relative attenuation
							// not absolute power!!
#define HW_MINP_40      160 // For LMS devices that have 40db output power range in .25dbunits)                     
#define HW_MINP_50      200 // For LMS devices that have 50db output power range
#define HW_MINP_55      220 // For LMS devices that have 55db output power range
#define HW_MINP_80      320 // The LMS-6123LH, LMS-802DX, LMS-183DX, and LMS-183CX have an 80db output power range

/// ---------- Const Defintions----------
char sVNX1[32] = "LMS-103";
char sVNX2[32] = "LMS-123";
char sVNX3[32] = "LMS-203";
char sVNX4[32] = "LMS-802";
char sVNX5[32] = "LMS-163";
char sVNX6[32] = "LMS-322D";
char sVNX7[32] = "LMS-232D";
char sVNX8[32] = "LMS-602D";
char sVNX9[32] = "LMS-402D";
char sVNX10[32] = "LMS-451D";
char sVNX11[32] = "LMS-271D";
char sVNX12[32] = "LMS-152D";
char sVNX13[32] = "LMS-751D";
char sVNX14[32] = "LMS-252D";
char sVNX15[32] = "LMS-6123LH";
char sVNX16[32] = "LMS-163LH";
char sVNX17[32] = "LMS-802LH";
char sVNX18[32] = "LMS-802DX";
char sVNX19[32] = "LMS-183DX";
char sVNX20[32] = "LMS-183CX";

unsigned short vnx_VID = 0x041f; // device VID for Vaunix products

const unsigned short LMS_Pids[21] = {
                    0x0000,  // unused
                    0x1220, // device PID for Vaunix LMS-103
                    0x1222, // device PID for Vaunix LMS-123
                    0x1223, // device PID for Vaunix LMS-203
                    0x1221, // device PID for Vaunix LMS-802
                    0x1224, // device PID for Vaunix LMS-163 
                    0x1225, // device PID for Vaunix LMS-322D
                    0x1226, // device PID for Vaunix LMS-232D
                    0x1227, // device PID for Vaunix LMS-602D
                    0x1228, // device PID for Vaunix LMS-402D
                    0x1229, // device PID for Vaunix LMS-451D
                    0x122A, // device PID for Vaunix LMS-271D
                    0x122B, // device PID for Vaunix LMS-152D
                    0x122C, // device PID for Vaunix LMS-751D
                    0x122D, // device PID for Vaunix LMS-252D
                    0x122E, // device PID for Vaunix LMS-6123LH
                    0x122F, // device PID for Vaunix LMS-163LH
                    0x1250, // device PID for Vaunix LMS-802LH
                    0x1251, // device PID for Vaunix LMS-802DX
                    0x1252, // device PID for Vaunix LMS-183DX
					0x1253}; // device PID for Vaunix LMS-183CX

// device types
#define LMS_103		1
#define LMS_123		2
#define LMS_203		3
#define LMS_802		4
#define LMS_163		5
#define LMS_322D	6
#define LMS_232D	7
#define LMS_602D	8
#define LMS_402D	9
#define LMS_451D	10
#define LMS_271D	11
#define LMS_152D	12
#define LMS_751D	13
#define LMS_252D	14
#define LMS_6123LH	15
#define LMS_163LH	16
#define LMS_802LH	17
#define LMS_802DX	18
#define LMS_183DX	19
#define LMS_183CX	20



/* -----------------------------------------------------------------
Global Data to maintain the Device's data & state for each process
-------------------------------------------------------------------*/
// globals we'll be using at runtime
char errmsg[32];		// For the status->string converter

bool bVerbose = FALSE;	// True to generate debug oriented printf output

bool TestMode = FALSE;	// if TestMode is true we fake it -- no HW access
						// TestMode defaults to FALSE for production builds

LMSPARAMS lms[MAXDEVICES];	// an array of structures each of which holds the info for a given 
							// device. The DeviceID is the index into the array. Devices may come and go
							// so there is no guarantee that the active elements are contiguous

// libusb 1.0 changes
int Trace_Out = 0;				// variable to control how much tracing to do
bool Did_libusb_init = FALSE;   // Flag used to prevent multiple calls to libusb_init, and to enable de-init when no devices are open
								// variables used to manage libusb 1.0 interaction
unsigned busnum = 0, devaddr = 0, _busnum, _devaddr;
libusb_device *dev, **devs;
libusb_device_handle *device = NULL;
struct libusb_device_descriptor desc;
libusb_context *LMS_ctx = NULL;

// we use timers for our read timeout, and to throttle the sending of USB commands
time_t starttime, currtime;
bool HasHRTimer = FALSE;
struct timespec mtimer_CTime, mtimer_LastSend;


/* stuff for the threads */
pthread_t threads[MAXDEVICES];

void *brick_handler_function(void * ptr);

char LibVersion[] = LIBVER;
//*****************************************************************************
//
// Function call for delay, usleep is deprecated, so this is a function using nanosleep()
//  
//*****************************************************************************
void catnap(long naptime) {
  // naptime comes in as the number of ms we want. 20 = 20ms
  struct timespec sleepytime;
   sleepytime.tv_sec = 0;
   sleepytime.tv_nsec = naptime * 1000000L;

   nanosleep(&sleepytime , NULL);
}


//*****************************************************************************
//
// Function call to check if the device is open or not
//  
//*****************************************************************************
bool CheckDeviceOpen(DEVID deviceID) {
    if (TestMode) return TRUE; // in test mode all devices are always available
    if ((lms[deviceID].DevStatus & DEV_OPENED) && (deviceID != 0))
        return TRUE;
    else
        return FALSE;
}

//*****************************************************************************
//
// Function call to check if the device is locked or not
//  
//*****************************************************************************
bool DevNotLocked(DEVID deviceID) {
    if (TestMode) return TRUE; // this shouldn't happen, but just in case...
    if (!(lms[deviceID].DevStatus & DEV_LOCKED))
        return TRUE; // we return TRUE if the device is not locked!
    else
        return FALSE;
}

//*****************************************************************************
//
// Function call to set the device status to locked
//  
//*****************************************************************************
void LockDev(DEVID deviceID, bool lock) {
    if (TestMode) return; // this shouldn't happen, but just in case...
    if (lock) {
        lms[deviceID].DevStatus = lms[deviceID].DevStatus | DEV_LOCKED;
        return;
    } else {
        lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_LOCKED;
        return;
    }
}

//*****************************************************************************
//
// Function call to display the error status as string
//  
//*****************************************************************************
char * fnLMS_perror(LVSTATUS status) {
    strcpy(errmsg, "STATUS_OK");
    if (BAD_PARAMETER == status) strcpy(errmsg, "BAD_PARAMETER");
    if (BAD_HID_IO == status) strcpy(errmsg, "BAD_HID_IO");
    if (DEVICE_NOT_READY == status) strcpy(errmsg, "DEVICE_NOT_READY");

    // Status returns for DevStatus
    if (INVALID_DEVID == status) strcpy(errmsg, "INVALID_DEVID");
    if (DEV_CONNECTED == status) strcpy(errmsg, "DEV_CONNECTED");
    if (DEV_OPENED == status) strcpy(errmsg, "DEV_OPENED");
    if (SWP_ACTIVE == status) strcpy(errmsg, "SWP_ACTIVE");
    if (SWP_UP == status) strcpy(errmsg, "SWP_UP");
    if (SWP_REPEAT == status) strcpy(errmsg, "SWP_REPEAT");
    if (SWP_BIDIRECTIONAL == status) strcpy(errmsg, "SWP_BIDIRECTIONAL");

    return errmsg;
}

//*****************************************************************************
//
// Function call to display float errors in string
//  
//*****************************************************************************
// A function to display the status as a string for functions that return a floating point value
char * fnLMS_pFloatError(float fstatus) {
    strcpy(errmsg, "STATUS_OK");
    if (fstatus == F_INVALID_DEVID) strcpy(errmsg, "INVALID_DEVID");
    if (fstatus == F_BAD_HID_IO) strcpy(errmsg, "BAD_HID_IO");
    if (fstatus == F_DEVICE_NOT_READY) strcpy(errmsg, "DEVICE_NOT_READY");

    return errmsg;
}

//*****************************************************************************
//
// Function call to get the library version in string form
//  
//*****************************************************************************
char * fnLMS_GetVersionStr(void) {
    return LibVersion;
}

//*****************************************************************************
//
// Function call to get the library version in integer form
//  
//*****************************************************************************

int fnLMS_GetLibVersion(void) {
    return LIBVER_NUM;
}

//*****************************************************************************
//
// Function call for open the lms device
//  
//*****************************************************************************
int VNXOpenDevice(DEVID deviceID) {

    int usb_status;
    int retries;
    int cnt, cntidx;
    libusb_device **devlist, *dev ;
    struct libusb_device_descriptor desc;
    int HWBusAddress = 0;
    
    if (!(lms[deviceID].DevStatus & DEV_CONNECTED)){ // we can't open a device that isn't there!
        return DEVICE_NOT_READY;
    }
        
    if (lms[deviceID].DevStatus & DEV_OPENED)        // the device is already open, BAD_PARAMETER gives a clue that it isn't just missing or disconnected
        return BAD_PARAMETER;

    if (!Did_libusb_init){
        usb_status = libusb_init(&LMS_ctx);     // we may need to re-init since we call libusb_exit when a user closes the last device
        if (usb_status != 0) 
            return BAD_HID_IO;
        Did_libusb_init = TRUE;
    }
    
    // we'll open the device. First we have to locate it by using its physical location where we enumerated it
    cnt = libusb_get_device_list(LMS_ctx, &devlist);

    for (cntidx=0; cntidx<cnt; cntidx++) {
        dev = devlist[cntidx];
        if (DEBUG_OUT > 2) printf("Device Search: cntidx=%d cnt=%d dev=%p\r\n", cntidx, cnt, (void *)dev);

        usb_status = libusb_get_device_descriptor(dev, &desc);

        // gather the USB address for this device so we can find it in the future
        HWBusAddress = ((libusb_get_bus_number(dev) << 8) | (libusb_get_device_address(dev)));

        if (HWBusAddress == lms[deviceID].BusAddress) {

            // we found the device, lets open it so the user can interact with it
            if (DEBUG_OUT > 2) printf("Opening LMS device %04x:%04x at bus address %04x\r\n",
                    desc.idVendor,
                    desc.idProduct,
                    HWBusAddress);

            usb_status = libusb_open(dev, &lms[deviceID].DevHandle);        
            
            if ((DEBUG_OUT > 2) && (usb_status != 0)) printf("  libusb_open returned error status = %x\r\n", usb_status);

            if (usb_status) {

                // our device opened failed, not much we can do about that except clean up and let the caller know
                libusb_free_device_list(devlist, 1);
                return DEVICE_NOT_READY;
            }

            if (DEBUG_OUT > 1) printf("  Detaching kernel driver %04x:%04x\r\n", lms[deviceID].idVendor, lms[deviceID].idProduct);
            usb_status = libusb_detach_kernel_driver(lms[deviceID].DevHandle, 0);

            if ((DEBUG_OUT > 2) && (usb_status != 0)) printf("  Detaching kernel driver returned error status = %x\r\n", usb_status);

            // In theory the libusb kernel driver might already be attached, so we ignore the LIBUSB_ERROR_NOT_FOUND case
            if ((usb_status != 0) && (usb_status != LIBUSB_ERROR_NOT_FOUND)) {

            libusb_free_device_list(devlist, 1);
            // we might have a disconnected device, so check for that
                if (usb_status == LIBUSB_ERROR_NO_DEVICE) {
                    lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_CONNECTED;
                    return DEVICE_NOT_READY;
                }

                return BAD_HID_IO;      // not necessarily IO layer, but something failed in the kernel stack below us
            }

            if (DEBUG_OUT > 2) printf("  Setting configuration %04x:%04x\r\n", lms[deviceID].idVendor, lms[deviceID].idProduct);
            usb_status = libusb_set_configuration (lms[deviceID].DevHandle, 1);

            if (DEBUG_OUT > 2) printf ("  successfully set configuration: %s\n", usb_status ? "failed" : "passed");

            if (usb_status != 0) {

                libusb_free_device_list(devlist, 1);
                // we might have a disconnected device, so check for that
                if (usb_status == LIBUSB_ERROR_NO_DEVICE) {
                    lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_CONNECTED;
                    return DEVICE_NOT_READY;
                }

                return BAD_HID_IO;      // not necessarily IO layer, but something failed in the kernel stack below us
            }

            usb_status = libusb_claim_interface (lms[deviceID].DevHandle, 0);

            if (DEBUG_OUT > 1) printf ("claim interface: %s\n", usb_status ? "failed" : "passed");

            if (usb_status != 0) {

                libusb_free_device_list(devlist, 1);
                // we might have a disconnected device, so check for that
                if (usb_status == LIBUSB_ERROR_NO_DEVICE) {
                    lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_CONNECTED;
                    return DEVICE_NOT_READY;
                }

                return BAD_HID_IO;      // not necessarily IO layer, but something failed in the kernel stack below us
            }

            // now we can start the read thread
            if (DEBUG_OUT > 2) printf("Starting thread %d\r\n", deviceID);
            lms[deviceID].thread_command = THREAD_START; /* open device and start processing */
            pthread_create(&threads[deviceID], NULL, brick_handler_function, (void*)(uintptr_t)deviceID);

            // wait here for the thread to start up and get out of the startup state
            retries = 10;
            while (retries && (lms[deviceID].thread_command == THREAD_START)) {
                catnap(100);  /* wait 100 ms */
                retries--;
            }

            if (lms[deviceID].thread_command == THREAD_START) {

                // for some reason our thread failed to startup and run, not much we can do but bail out
                libusb_free_device_list(devlist, 1);
                lms[deviceID].thread_command = THREAD_EXIT;

                // wait here for the thread to exit on its own
                retries = 10;
                while (retries && (lms[deviceID].thread_command != THREAD_DEAD)) {
                    catnap(100);  /* wait 100 ms */
                    retries--;
                }

                // the thread didn't exit on its own, so just cancel it
                if (lms[deviceID].thread_command != THREAD_DEAD) {
                    pthread_cancel(threads[deviceID]);
                }

                return BAD_HID_IO;

            }

            // Mark the device as opened
            lms[deviceID].DevStatus = lms[deviceID].DevStatus | DEV_OPENED;

            // we only open one device, so we are done now
            libusb_free_device_list(devlist, 1);
            return STATUS_OK;

        }   // end of matching bus address case
    }

    // we failed to find a matching device if we get here
    libusb_free_device_list(devlist, 1);
    return DEVICE_NOT_READY;
}

//*****************************************************************************
//
// Function call report_data_decode, used to parse responses from the LMS device
//  
//*****************************************************************************
void report_data_decode(unsigned char rcvdata[], int tid) {
    int i;
    unsigned long dataval;
    char temp[32];
    
    if (DEBUG_OUT > 2) {
        printf("Decoding ");
        for (i = 0; i < 8; i++)
            printf("%02x ", rcvdata[i]);
        printf("\r\n");
    }

    /*  the first byte tells us the type, the second is the data length
        tid is the thread ID it came from so we can stash the value into lms[]
        first decode the bytes
    */
    dataval = 0;
    if (0 < rcvdata[1]) {
        for (i = 0; i < rcvdata[1]; i++)
            dataval = (dataval << 8) + rcvdata[1 + rcvdata[1] - i];
    }
    if (DEBUG_OUT > 1){
        printf("Data payload decodes to %ld (%08x)\r\n", dataval, (unsigned int)dataval);
    }
    /* now we'll assign it to lms[] */

    // handle the status report
    switch (rcvdata[0]) {
    case VNX_DSS_STATUS:
        if (DevNotLocked(tid)) {
            lms[tid].Frequency = rcvdata[2] + (rcvdata[3] << 8) + (rcvdata[4] << 16) + (rcvdata[5] << 24); // update the frequency
            if (DEBUG_OUT > 2) printf("Status decode device = %d, Frequency = %d\r\n", tid, lms[tid].Frequency);
            if (DEBUG_OUT > 1) printf(".");
            fflush(0);

            // decode the status report and update our device status
            if (rcvdata[6] & (STATUS_PLL_LOCK)) // is the PLL locked?
                lms[tid].DevStatus = lms[tid].DevStatus | PLL_LOCKED;
            else
                lms[tid].DevStatus = lms[tid].DevStatus & ~PLL_LOCKED;

            if (rcvdata[6] & (SWP_ONCE | SWP_CONTINUOUS)) // are we sweeping?
                lms[tid].DevStatus = lms[tid].DevStatus | SWP_ACTIVE;
            else
                lms[tid].DevStatus = lms[tid].DevStatus & ~SWP_ACTIVE;

            if (rcvdata[6] & (SWP_DIRECTION)) // are we sweeping down in frequency?
                lms[tid].DevStatus = lms[tid].DevStatus & ~SWP_UP;
            else
                lms[tid].DevStatus = lms[tid].DevStatus | SWP_UP; // set the SWP_UP status bit if we are sweeping upwards

            if (rcvdata[6] & (SWP_CONTINUOUS)) // are we in continuous sweep mode?
                lms[tid].DevStatus = lms[tid].DevStatus | SWP_REPEAT;
            else
                lms[tid].DevStatus = lms[tid].DevStatus & ~SWP_REPEAT;

            if (rcvdata[6] & (SWP_BIDIR)) // are we in bi-directional sweep mode?
                lms[tid].DevStatus = lms[tid].DevStatus | SWP_BIDIRECTIONAL;
            else
                lms[tid].DevStatus = lms[tid].DevStatus & ~SWP_BIDIRECTIONAL;

        } /* if devnotlocked() */

        if (DEBUG_OUT > 2) printf("DevStatus at the end of ParseHidPacket: %d \r\n", (lms[tid].DevStatus & DEV_RDTHREAD));
        break;

    case VNX_DSS_FREQUENCY:
        if (DEBUG_OUT > 0) printf(" Frequency = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].Frequency = dataval;
        break;

    case VNX_DSS_SWPTIME:
        if (DEBUG_OUT > 0) printf(" Sweep Time = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].SweepTime = dataval;
        break;

    case VNX_DSS_FSTART:
        if (DEBUG_OUT > 0) printf(" Sweep Start Frequency = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].StartFrequency = dataval;
        break;

    case VNX_DSS_FSTOP:
        if (DEBUG_OUT > 0) printf(" Sweep End Frequency = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].EndFrequency = dataval;
        break;

	case VNX_FSTEP:
        if (DEBUG_OUT > 0) printf(" Sweep Frequency step = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].FrequencyStep = dataval;
        break;
		
    case VNX_SWEEP:
        if (DEBUG_OUT > 0) printf(" Sweep Mode = %d\r\n", rcvdata[2]);
        if (DevNotLocked(tid))
            lms[tid].Frequency = dataval; 
        break;

    case VNX_ONTIME:
        if (DEBUG_OUT > 0) printf(" Pulse On Time = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].PulseOnTime = dataval;
        break;

    case VNX_OFFTIME:
        if (DEBUG_OUT > 0) printf(" Pulse Off Time = %ld\r\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].PulseOffTime = dataval;
        break;

    case VNX_PULSE_MODE:
        if (DEBUG_OUT > 0) printf(" Pulse Mode = %x\r\n", rcvdata[2]);
        dataval = rcvdata[2] & 0x03; // make sure we only get two bits of mode
        if (DevNotLocked(tid)) {
            if (rcvdata[2] & 0x80) // the bit is true when the fast PWM option is not present
            {
                lms[tid].DevStatus = lms[tid].DevStatus & ~FAST_PULSE_OPTION;
            } else // the option is present
            {
                lms[tid].DevStatus = lms[tid].DevStatus | FAST_PULSE_OPTION;
            }
            if (rcvdata[2] & 0x40) // the bit is true when the fast PWM option _is_ present
            {
                lms[tid].DevStatus = lms[tid].DevStatus | EXT_SWEEP_OPTION;
            } else // the option is not present
            {
                lms[tid].DevStatus = lms[tid].DevStatus & ~EXT_SWEEP_OPTION;
            }
            lms[tid].Modebits = lms[tid].Modebits & ~PWM_MASK; // clear the PWM bitfield
            dataval <<= 8; // align the PWM bits with their bitfield
            lms[tid].Modebits = lms[tid].Modebits | dataval; // or in the PWM mode bits
        }
        break;
	
	case VNX_CHIRP_RATE:
		if (DEBUG_OUT > 0)printf(" HW reports chirp repeat rate = %ld\r\n", dataval);
		if (DevNotLocked(tid))
			lms[tid].ChirpRepeatRate = dataval;
		break;

	case VNX_CHIRP_LENGTH:
		if (DEBUG_OUT > 0)printf(" HW reports chirp length = %ld\n", dataval);
		if (DevNotLocked(tid))
			lms[tid].ChirpLength = dataval;
		break;	

    case VNX_RFMUTE:
        if (rcvdata[2]) strcpy(temp, "RF ON");
        else strcpy(temp, "RF OFF");
        if (DEBUG_OUT > 0) printf("%s \n", temp);

        if (DevNotLocked(tid)) {
            if (rcvdata[2]) {
                lms[tid].Modebits = lms[tid].Modebits | MODE_RFON;
            } else {
                lms[tid].Modebits = lms[tid].Modebits & ~MODE_RFON;
            }
        }
        break;

    case VNX_INTOSC:
        if (rcvdata[2]) strcpy(temp, "Using Internal Osc");
        else strcpy(temp, "Using External Osc");
        if (DEBUG_OUT > 0) printf("%s \r\n", temp);

        if (DevNotLocked(tid)) {
            if (rcvdata[2]) {
                lms[tid].Modebits = lms[tid].Modebits | MODE_INTREF;
            } else {
                lms[tid].Modebits = lms[tid].Modebits & ~MODE_INTREF;
            }
        }
        break;

    case VNX_EXTSWP:
        if (rcvdata[2]) strcpy(temp, "Using External Sweep Trigger");
        else strcpy(temp, "Using Internal Sweep Trigger");
        if (DEBUG_OUT > 0) printf("%s \r\n", temp);

        if (DevNotLocked(tid)) {
            if (rcvdata[2]) {
                lms[tid].Modebits = lms[tid].Modebits | MODE_EXTSWEEP;
            } else {
                lms[tid].Modebits = lms[tid].Modebits & ~MODE_EXTSWEEP;
            }
        }
        break;

    case VNX_PWR:
        if (DEBUG_OUT > 0) printf(" Raw Power Setting = %d units)\n", rcvdata[2]);
        if (DevNotLocked(tid))
            lms[tid].Power = lms[tid].MaxPower - (int) rcvdata[2] * lms[tid].PowerScale; // API is always in .25db units, HW reports vary

        break;

    case VNX_MAX_PWR:
        if (DEBUG_OUT > 0) printf(" Max Power Setting = %d units)\n", rcvdata[2]);
        if (DevNotLocked(tid))
            lms[tid].MaxPower = (int) rcvdata[2]; // API and HW is in .25db units
        break;

    case VNX_MINFREQUENCY:
        if (DEBUG_OUT > 0) printf(" Minimum Frequency = %ld\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].MinFrequency = dataval * 10000; // our device returns MinFrequency in 100 KHz units
        break;

    case VNX_MAXFREQUENCY:
        if (DEBUG_OUT > 0) printf(" Maximum Frequency = %ld\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].MaxFrequency = dataval * 10000; // same as MinFrequency
        break;

    case VNX_GETSERNUM:
        if (DEBUG_OUT > 0) printf(" Serial Number = %ld\n", dataval);
        if (DevNotLocked(tid))
            lms[tid].SerialNumber = dataval; // NB -- we never use this path!
        break;
    } /* switch */

    return;
}

//*****************************************************************************
//
// Function call for thread handler for the brick 
//  
//*****************************************************************************
void *brick_handler_function(void * threadID) {
    int i, tid;
    tid = (int)(uintptr_t)threadID;
    int usb_status;
    char fullpath[128];
    int retries;
    int cnt, cntidx;
    int xfer_length;
    libusb_device **devlist, *dev ;
    libusb_device_handle *devhandle = NULL;

    if (DEBUG_OUT > 0) printf("Starting thread for device %d\r\n", tid);
    
    // -- top level thread loop --
    while ((lms[tid].thread_command >= 0) &&(lms[tid].thread_command != THREAD_EXIT)) {
        switch (lms[tid].thread_command) {
        case THREAD_IDLE:
            usb_status = -1;
            retries = 50;
            while ((usb_status < 0) && (retries--) && (THREAD_IDLE == lms[tid].thread_command)) {
                if (DEBUG_OUT > 2) printf("Calling libusb_interrupt_transfer with DevHandle = %p, Endpoint = %x, rcvbuff = %p\r\n",(void *)lms[tid].DevHandle, lms[tid].Endpoint, (void *)lms[tid].rcvbuff);

                    usb_status = libusb_interrupt_transfer(lms[tid].DevHandle,  // device handle
                                lms[tid].Endpoint,  // endpoint
                                (unsigned char *)lms[tid].rcvbuff,  // buffer
                                PACKET_INT_LEN,     // max length
                                &xfer_length,       // could be actual transfer length
                                TIMEOUT);

                    if (usb_status == LIBUSB_ERROR_NO_DEVICE) {

                        // our device was unplugged, so we should clean up and end this thread
                        lms[tid].DevStatus = lms[tid].DevStatus & ~DEV_CONNECTED;

                        lms[tid].thread_command = THREAD_EXIT;
                    }

                    if (usb_status < 0) catnap(20); /* wait 20 ms before trying again */
                  }
			      if (DEBUG_OUT > 0) printf("THREAD_IDLE -usb_state:%d\t xfer_length:%d\n",usb_status,xfer_length);
                  // did we get some data?
                  if ((usb_status == 0) && (xfer_length > 0)) {
                    if (DEBUG_OUT > 2) {
                        printf("Thread %d reports %d...", tid, usb_status);
                            for (i=0; i<usb_status; i++)
                            printf("%02x ", lms[tid].rcvbuff[i]);
                        printf("\r\n");
                    }
                    /* decode the HID data */
                    report_data_decode((unsigned char *)lms[tid].rcvbuff, tid);
                    if (DEBUG_OUT > 2) printf("Decoded device %d data %02x, decodewatch=%02x\r\n", tid, lms[tid].rcvbuff[0], lms[tid].decodewatch);
                    if (lms[tid].decodewatch == lms[tid].rcvbuff[0]) {
                    if (DEBUG_OUT > 2) printf("Clearing decodewatch %02x for thread %d\r\n", lms[tid].decodewatch, tid);
                    lms[tid].decodewatch = 0;
                    }
                  } else
                    if (DEBUG_OUT > 0) perror("THREAD_IDLE");
                  break;
      
        case THREAD_START:
                // transition to the read state
                lms[tid].thread_command = THREAD_IDLE;
            break;
        } /* switch */
    } /* while */

  // -- we received a THREAD_EXIT command
//  if (DEBUG_OUT > 0) printf("Exiting thread for device %d because command=%d\r\n", tid, lms[tid].thread_command);
  lms[tid].thread_command = THREAD_DEAD;
  pthread_exit(NULL);

}

//*****************************************************************************
//
// Function call to Send data to device
//  
//*****************************************************************************
bool SendReport(int deviceID, char command, char *pBuffer, int cbBuffer) {
    int i;
    int send_status;
    int retries;
    int lastSendSeconds;
    long lastSendNanoSeconds;
    
    // Make sure the buffer that is being passed to us fits
    if (cbBuffer > HR_BLOCKSIZE) {
        // Report too big, don't send!
        return FALSE;
    }

    // lets make sure our command doesn't have any junk bits in it
    for (i=0; i<HID_REPORT_LENGTH; i++)
        lms[deviceID].sndbuff[i] = 0;

    if (DEBUG_OUT > 1) printf("SR: command=%x cbBuffer=%d\r\n", (uint8_t)command, cbBuffer);
    lms[deviceID].sndbuff[0] = command; // command to device
    lms[deviceID].sndbuff[1] = cbBuffer;
    for (i=0; i<cbBuffer; i++)
        lms[deviceID].sndbuff[i+2] = pBuffer[i];
    
    if (DEBUG_OUT > 1) {
        printf("SR: ");
        for (i = 0; i < 8; i++) {
            printf("%02x ",  (uint8_t)lms[deviceID].sndbuff[i]);
        }
        printf("\r\n");
    }

    // we wait for a file handle to appear in case we get here before the device really opened (unlikely...)
    retries = 0;
    while ((0 == lms[deviceID].DevHandle) && (retries++ < 10)){
        sleep(1);
    }

    // we may have to wait in order to ensure that very fast USB host controllers don't overrun our device
    if (HasHRTimer){
        clock_gettime(CLOCK_MONOTONIC_COARSE, &mtimer_CTime);
        lastSendSeconds = mtimer_CTime.tv_sec - mtimer_LastSend.tv_sec;
        lastSendNanoSeconds = mtimer_CTime.tv_nsec - mtimer_LastSend.tv_nsec;

        if (DEBUG_OUT > 2) printf("SR: last send was %d seconds and %ld nanoseconds ago\r\n", lastSendSeconds, lastSendNanoSeconds);

        if (lastSendSeconds == 1){
            // we may have rolled over, so check if our last time was at least 1 ms earlier
            lastSendNanoSeconds = mtimer_CTime.tv_nsec + (1000000000L - mtimer_LastSend.tv_nsec);
            if (lastSendNanoSeconds < 1000000L){
                // we want to wait enough to allow 1ms between USB Commands
                mtimer_CTime.tv_sec = 0;
                mtimer_CTime.tv_nsec = 1000000L - lastSendNanoSeconds;
                if (DEBUG_OUT > 2) printf("SR: rollover delay = %ld\r\n", mtimer_CTime.tv_nsec);
                nanosleep(&mtimer_CTime , NULL);
            }
        }
        else if (lastSendSeconds == 0 && lastSendNanoSeconds < 1000000L) {
            // we want to wait enough to allow 1ms between USB Commands
            mtimer_CTime.tv_sec = 0;
            mtimer_CTime.tv_nsec = 1000000L - lastSendNanoSeconds;
            if (DEBUG_OUT > 2) printf("SR: delay = %ld\r\n", mtimer_CTime.tv_nsec);
            nanosleep(&mtimer_CTime , NULL);
        }
        else {
            // if lastSendSeconds > 1 or the nanoseconds portion is > 1 ms we don't have to worry about it...
            if (DEBUG_OUT > 2) printf("SR: no delay needed, last send was %d seconds and %ld nanoseconds ago\r\n", lastSendSeconds, lastSendNanoSeconds);
        }
    }
    else {
        // we don't have a timer, so we'll just wait 1ms
        mtimer_CTime.tv_sec = 0;
        mtimer_CTime.tv_nsec = 1000000L;
        if (DEBUG_OUT > 2) printf("SR: no HR time fixed delay = %ld\r\n", mtimer_CTime.tv_nsec);
        nanosleep(&mtimer_CTime , NULL);
    }

    // update the send time
    clock_gettime(CLOCK_MONOTONIC_COARSE, &mtimer_LastSend);

    /* we have data to write to the device */
    if (DEBUG_OUT > 1) printf("SR: sending the write to handle %p...\r\n", (void *)lms[deviceID].DevHandle);
    send_status = libusb_control_transfer(lms[deviceID].DevHandle,
                0x21,
                0x09, //HID_REPORT_SET,
                0x200,
                0,
                (unsigned char *)lms[deviceID].sndbuff,
                PACKET_CTRL_LEN,
                TIMEOUT);
    if (DEBUG_OUT > 1) printf("SR: sent it...\r\n");
    if (DEBUG_OUT > 1) {
        printf("(status=%d handle=%p)", send_status, (void *)lms[deviceID].DevHandle);
        if (send_status < 0) perror("SendReport"); else printf("\r\n");
    }
    return TRUE;
}

//*****************************************************************************
//
// Function call to read a parameter from the device
//  
//*****************************************************************************
// ------------ GetParameter ---------------------------------------------
//
// The GetParam argument is the command byte sent to the device to get
// a particular value. The response is picked up by the read thread and
// parsed by it. The parser clears the corresponding event.

bool GetParameter(int deviceID, int GetParam) {
    char VNX_param[6] = {0, 0, 0, 0, 0, 0};
    int timedout;

    if (DEBUG_OUT > 0) printf(" sending a GET command = %x\n", (char) GetParam );
    lms[deviceID].decodewatch = (char) GetParam;
    if (!SendReport(deviceID, (char)GetParam, VNX_param, 0)) {
        return FALSE;
    }

    if (DEBUG_OUT > 0) printf(" SendReport sent a GET command successfully to device %d = %x\n", deviceID, (char) GetParam );

    starttime = time(NULL);
    timedout = 0;

    while ((lms[deviceID].decodewatch > 0) && (0 == timedout)) {

      catnap(10);   // yield for 10 ms
      if ((time(NULL)-starttime) > 2) timedout = 1;
    }

    return (0 == timedout) ? TRUE : FALSE;
}

// -------------- Get Routines to read device settings --------------------
//*****************************************************************************
//
// Function call to read the frequency in 10Hz units
//  
//*****************************************************************************
bool GetFrequency(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_DSS_FREQUENCY))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the power level in 0.25 db units
//  
//*****************************************************************************
bool GetPower(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_PWR))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the sweep frequency start in 10 Hz units
//  
//*****************************************************************************
bool GetFStart(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_DSS_FSTART))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the sweep frequency end
//  
//*****************************************************************************
bool GetFEnd(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_DSS_FSTOP))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the sweep control bits
//  
//*****************************************************************************
bool GetSweep(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_DSS_SWPTIME))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the Max Power available in 0.25 db units
//  
//*****************************************************************************
bool GetMaxPower(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_MAX_PWR))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the RF status (on/off)
//  
//*****************************************************************************
bool GetRF_On(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_RFMUTE))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read which reference oscillator is in use
//  
//*****************************************************************************
bool GetUseIntOsc(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_INTOSC))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read if the external sweep trigger is enabled
//  
//*****************************************************************************
bool GetUseExtSwpTrigger(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_EXTSWP))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the device's minimum frequency in 10 Hz units
//  
//*****************************************************************************
bool GetFMinimum(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_MINFREQUENCY))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the device's maximum frequency in 10 Hz units
//  
//*****************************************************************************
bool GetFMaximum(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_MAXFREQUENCY))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the pulse on time
//  
//*****************************************************************************
bool GetPulseOnTime(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_ONTIME))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the pulse off time
//  
//*****************************************************************************
bool GetPulseOffTime(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_OFFTIME))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the pulse mode
//  
//*****************************************************************************
bool GetPulseMode(DEVID deviceID) {
    if (!GetParameter(deviceID, VNX_PULSE_MODE))
        return FALSE;
    return TRUE;
}

//*****************************************************************************
//
// Function call to read the device's serial number
//  
//*****************************************************************************
bool GetSerNum(DEVID deviceID) {
  if (!GetParameter(deviceID, VNX_GETSERNUM))
    return FALSE;

  return TRUE;
}

//*****************************************************************************
//
// Function call to read the chirp length
//  
//*****************************************************************************
bool GetChirpLength(DEVID deviceID) {
  if (!GetParameter(deviceID, VNX_CHIRP_LENGTH))
    return FALSE;

  return TRUE;
}

//*****************************************************************************
//
// Function call to read the chirp repeat rate
//  
//*****************************************************************************
bool GetChirpRepeatRate(DEVID deviceID) {
  if (!GetParameter(deviceID, VNX_CHIRP_RATE))
    return FALSE;

  return TRUE;
}




/* functions to manage devices, not getting or retrieving data */
/*-------------------------------------------------------------*/

// function to get the serial number of the device, returns negative values for errors, or the actual serial number
// certain LMS-802 devices return a different USB serial number string than their actual serial number
// This function returns the actual device serial number. While it is theoretically possible for a device to have a serial number of 0,
// this code assumes non-zero serial numbers.
//
// This function is called with an open devhandle and leaves it open for the caller to close
//
//*****************************************************************************
//
// Function call to get serial number if vaunix device
//  
//*****************************************************************************
int GetSerialNumber(libusb_device_handle *devhandle, int HWType, uint8_t sernum_string_desc_index, char* str_sernum)
{
    int i;
    int usb_status;
    int serial_number = 0;
    char rcvbuff[32];
    int TDev = 0;
    int retries;

    // quick sanity check
    if (devhandle == NULL) return DEVICE_NOT_READY;     // somehow we don't have a valid device handle, so bail out now

    // protect our receive buffer
    for (i=0; i<32; i++) rcvbuff[i]=0;

    // get the USB serial number string
    usb_status = libusb_get_string_descriptor_ascii(devhandle, sernum_string_desc_index, (unsigned char *)rcvbuff, sizeof(rcvbuff)-1);

    if (DEBUG_OUT > 1) printf("SerNum string %d = [%s]\r\n", sernum_string_desc_index, rcvbuff);
    if (usb_status < 0) return BAD_HID_IO;

    // the USB serial number string starts with SN: so we skip those characters
    serial_number = atoi(rcvbuff+3);

    // we can just use the USB serial number value
    if (str_sernum != NULL)
        strcpy(str_sernum, rcvbuff+3);

    return serial_number;
}

//*****************************************************************************
//
// Function call to find VNX LMS Devices on the usb buses
//  
//*****************************************************************************
void FindVNXDevices() {

    bool bFound;
    int hTemp;			// temporary variable
    int HWType;			// temporary variable for hardware/model type
    int HWMinFrequency; // temporary variable for default minimum frequency
    int HWMaxFrequency; // temporary variable for default maximum frequency
    int HWMaxPower; 	// temporary variable for default maximum power
    int HWMinPower; 	// temporary variable for default minimum power
    int HWPowerScale; 	// temporary variable for power control command scaling
	bool HWHasChirp; 	// true for HW that can chirp
    char HWName[32]; 	// temporary variable for the hardware model name
    int HWSerialNumber;	// temporary holder for the device serial number
    char sendbuff[8];
    char rcvbuff[32];
    int usb_status;
    int HWBusAddress;	// temporary holder for the USB bus address of the device, used to find the device later
    int HWEndpoint;		// temporary holder for the HW endpoint used for status reports
    libusb_device_handle *devhandle = NULL;
    int busnum;
    int busaddress;
    
	if (DEBUG_OUT > 2)  printf("In FindVNXDevices\r\n");

    if (!Did_libusb_init){
        usb_status = libusb_init(&LMS_ctx);     // just in case...
        if (usb_status != 0) return;            // we can't really return an error, but hopefully we won't just crash
        Did_libusb_init = TRUE;
    }

	if (DEBUG_OUT > 2)  printf("In FindVNXDevices after libusb_init\r\n");

    libusb_device **devlist, *dev ;
    int r;
    ssize_t cnt;
    int c, a, j;
    unsigned int i;
    int send_status, open_status;
    int cntidx;

    /* ... */

    // We need to remove devices from our table that are no longer connected ---
    // to do this we clear the "connected" flag for each table entry that is not open initially
    // then, as we find them we re-set the "connected" flag
    // anybody who doesn't have his "connected" flag set at the end is gone - we found it
    // previously but not this time

    for (i = 1; i < MAXDEVICES; i++) {
        if ((lms[i].SerialNumber != 0) && !(lms[i].DevStatus & DEV_OPENED))
            lms[i].DevStatus = lms[i].DevStatus & ~DEV_CONNECTED;
    }

    // Grab the list of devices and see if we like any of them
    cnt = libusb_get_device_list(NULL, &devlist);
	if (DEBUG_OUT > 2) printf("libusb found %ld usb devices\r\n", cnt);

    for (cntidx=0; cntidx<cnt; cntidx++) {
        dev = devlist[cntidx];
        struct libusb_device_descriptor desc;

        usb_status = libusb_get_device_descriptor(dev, &desc);
        if (DEBUG_OUT > 1) printf("Vendor: %04x PID: %04x (%d of %ld)\r\n", desc.idVendor, desc.idProduct, cntidx, cnt);

		// pre-set the common cases
        HWType = 0;
		HWMaxPower = HW_MAXP_10;		// +10db output power
        HWEndpoint = ENDPOINT_INT_IN2;
		HWHasChirp = FALSE;
		
        // check to see if the device is one of our devices
        if (desc.idVendor == vnx_VID) {
            // look through our PIDs to see if any match the device
            for (i = 1; i<(sizeof LMS_Pids / sizeof LMS_Pids[0]); i++) {
                if (LMS_Pids[i] == desc.idProduct){
                    HWType = i;
                    break;
                }
            }
        }
    
        if (DEBUG_OUT > 2) printf("Found HWType = %d\r\n", HWType);

        if (HWType) { /* we like this device and we'll keep it */
            // gather the USB address for this device so we can find it in the future
            busnum = (int)libusb_get_bus_number(dev);
            busaddress = (int)libusb_get_device_address(dev);
            if (DEBUG_OUT > 2) printf("USB bus = %d, address =%d\r\n", busnum, busaddress);

            HWBusAddress = (busnum << 8) | busaddress;

            if (DEBUG_OUT > 1) printf("Opening LMS device %04x:%04x type %d at bus address %02x\r\n",
                    desc.idVendor,
                    desc.idProduct,
                    HWType, HWBusAddress);

            // lets open the device to gather more information about it
            usb_status = libusb_open(dev, &devhandle);
            if (usb_status < 0) {
                if (DEBUG_OUT > 0)  printf("FindVNXDevices sez: I can't open the device!\r\n");
                // The most likely cause of the libusb_open failure is a device we already have open for an application -- so skip over it
                continue;
            }
            else {
                switch (HWType) {
                case LMS_103:								// LMS-103
                    strcpy(HWName, sVNX1);
                    HWMinFrequency = 500000000;				// 5 GHz in 10Hz units
                    HWMaxFrequency = 1000000000;			// 10 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-103 has a 50db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_123:								// LMS-123
                    strcpy(HWName, sVNX2);
                    HWMinFrequency = 800000000;				// 8 GHz min
                    HWMaxFrequency = 1200000000;			// 12 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-123 has a 50db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_203:								// LMS-203
                    strcpy(HWName, sVNX3);
                    HWMinFrequency = 1000000000;			// 10 GHz min
                    HWMaxFrequency = 2000000000;			// 20 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_40;	// the LMS-203 has a 40db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_802:								// LMS-802
                    strcpy(HWName, sVNX4);
                    HWMinFrequency = 400000000;				// 4 GHz min
                    HWMaxFrequency = 800000000;				// 8 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-802 has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_163:								// LMS-163
                    strcpy(HWName, sVNX5);
                    HWMinFrequency = 800000000;				// 8 GHz min
                    HWMaxFrequency = 1600000000;			// 16 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_40;	// the LMS-163 has a 40db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_322D:								// LMS-322D
                    strcpy(HWName, sVNX6);
                    HWMinFrequency = 60000000;				// .6 GHz min
                    HWMaxFrequency = 230000000;				// 2.3 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_55;	// the LMS-322D has a 55 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_232D:								// LMS-232D
                    strcpy(HWName, sVNX7);
                    HWMinFrequency = 50000000;				// .5 GHz min
                    HWMaxFrequency = 230000000;				// 2.3 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-232D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_602D:								// LMS-602D
                    strcpy(HWName, sVNX8);
                    HWMinFrequency = 150000000;				// 1.5 GHz min
                    HWMaxFrequency = 600000000;				// 6 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-602D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_402D:								// LMS-402D
                    strcpy(HWName, sVNX9);
                    HWMinFrequency = 100000000;				// 1 GHz min
                    HWMaxFrequency = 400000000;				// 4 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-402D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_451D:								// LMS-451D
                    strcpy(HWName, sVNX10);
                    HWMinFrequency = 7000000;				// 70  MHz min
                    HWMaxFrequency = 45000000;				// 450 MHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_55;	// the LMS-451D has a 55 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_271D:								// LMS-271D
                    strcpy(HWName, sVNX11);
                    HWMinFrequency = 50000;					// 500 KHz min
                    HWMaxFrequency = 27000000;				// 270 MHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// the LMS-271D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_152D:								// LMS-152D
                    strcpy(HWName, sVNX12);
                    HWMinFrequency = 25000000;				// 250 MHz min
                    HWMaxFrequency = 150000000;				// 1.5 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_55;	// the LMS-152D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_751D:								// LMS-751D
                    strcpy(HWName, sVNX13);
                    HWMinFrequency = 10000000;				// 100 MHz min
                    HWMaxFrequency = 75000000;				// 750 MHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// RD!!! the LMS-751D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_252D:								// LMS-252D
                    strcpy(HWName, sVNX14);
                    HWMinFrequency = 40000000;				// 400 MHz min
                    HWMaxFrequency = 250000000;				// 2.5 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_50;	// RD!!! the LMS-252D has a 50 db output power range
                    HWPowerScale = 1;						// the device uses .25db units 
                    break;
                case LMS_6123LH:							// LMS-6123LH
                    strcpy(HWName, sVNX15);
                    HWMinFrequency = 600000000;				// 6 GHz min
                    HWMaxFrequency = 1200000000;			// 12 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// the LMS-6123LH has a 70 db output power range
                    HWPowerScale = 2;						// the device uses .5db units 
                    break;
                case LMS_163LH:								// LMS-163LH
                    strcpy(HWName, sVNX16);
                    HWMinFrequency = 800000000;				// 8 GHz min
                    HWMaxFrequency = 1600000000;			// 16 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// the LMS-163LH has a 70 db output power range
                    HWPowerScale = 2;						// the device uses .5db units 
                    break;
                case LMS_802LH:								// LMS-802LH
                    strcpy(HWName, sVNX17);
                    HWMinFrequency = 200000000;				// 2 GHz min
                    HWMaxFrequency = 800000000;				// 8 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// RD!!! the LMS-802LH has a 70 db output power range
                    HWPowerScale = 2;						// the device uses .5db units 
                    break;
                case LMS_802DX:								// LMS-802DX
                    strcpy(HWName, sVNX18);
                    HWMinFrequency = 200000000;				// 2 GHz min
                    HWMaxFrequency = 800000000;				// 8 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// the LMS-802DX has a 70 db output power range
                    HWPowerScale = 2;						// the device uses .5db units 
                    break;
                case LMS_183DX:								// LMS-183DX
                    strcpy(HWName, sVNX19);
                    HWMinFrequency = 600000000;				// 6 GHz min
                    HWMaxFrequency = 1800000000;			// 18 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// the LMS-183DX has a 70 db output power range
                    HWPowerScale = 2;						// the device uses .5db units 
                    break;
				case LMS_183CX:								// LMS-183CX
                    strcpy(HWName, sVNX20);
                    HWMinFrequency = 600000000;				// 6 GHz min
                    HWMaxFrequency = 1800000000;			// 18 GHz max
                    HWMinPower = HW_MAXP_10 - HW_MINP_80;	// the LMS-183cX has a 70 db output power range
                    HWPowerScale = 2; 						// the device uses .5db units 
					HWHasChirp = TRUE;
					HWEndpoint = ENDPOINT_INT_IN1;
                    break;

                } /* HWType switch */

                // Lets get the device's serial number so we can uniquely identify it
                // We can also use the USB BusNumber:Address to identify devices

                HWSerialNumber = GetSerialNumber(devhandle, HWType, desc.iSerialNumber, rcvbuff);

                // We should be done with the device now
                libusb_close(devhandle);

                if (HWSerialNumber < 0) continue;       // without a serial number we can't do anything with the device
        
                /* find an open slot to save the data */
                // lets see if we have this unit in our table of devices already
                bFound = FALSE;

                for (i = 1; i < MAXDEVICES; i++) {
                    if (lms[i].SerialNumber == HWSerialNumber) {
                        // we already have the device in our table
                        bFound = TRUE;
                        lms[i].DevStatus = lms[i].DevStatus | DEV_CONNECTED; // its here, mark it as connected
                        // at this point the device is present, but not in use, no sense looking more
                        break;
                    }
                } // end of for loop

                // if the device isn't in the table we need to add it
                if (!bFound) {
                    hTemp = 0;
                    for (i = 1; i < MAXDEVICES; i++) {
                        if (lms[i].SerialNumber == 0) {
                            hTemp = i;
                            break;
                        }
                    } // end of for loop search for an empty slot in our array of devices

                    /* save all of the data we've already acquired */
                    if (hTemp) {
                        lms[hTemp].LMSType = HWType;								 // save the device type to simplify any device specific IO
                        lms[hTemp].SerialNumber = HWSerialNumber;					 // save the device's serial number
                        lms[hTemp].DevStatus = lms[hTemp].DevStatus | DEV_CONNECTED; // mark it as present
                        strcpy(lms[hTemp].ModelName, HWName);						 // save the device's model name
                        lms[hTemp].MinFrequency = HWMinFrequency;
                        lms[hTemp].MaxFrequency = HWMaxFrequency;					 // default values for frequency range
                        lms[hTemp].MaxPower = HWMaxPower;							 // default value for maximum power
                        lms[hTemp].MinPower = HWMinPower;							 // and minimum power (adjusted after we read max pwr from device)
                        lms[hTemp].PowerScale = HWPowerScale;						 // we need to track the units for each device

                        // The device has been closed so let's make sure we can find it again
                        // We use the BusAddress for opens by the user after detection
                        lms[hTemp].idVendor = desc.idVendor;
                        lms[hTemp].idProduct = desc.idProduct;
                        lms[hTemp].idType = HWType;
                        lms[hTemp].BusAddress = HWBusAddress;
                        strcpy(lms[hTemp].Serialstr, rcvbuff);
						
						// we may have to adjust the endpoint based on underlying HW
						if ((HWType == LMS_183DX) && (HWSerialNumber > 26000)) HWEndpoint = ENDPOINT_INT_IN1;
						if ((HWType == LMS_802DX) && (HWSerialNumber > 33687)) HWEndpoint = ENDPOINT_INT_IN1;
						lms[hTemp].Endpoint = HWEndpoint;
						
                        if (DEBUG_OUT > 1) {
                            printf("Stored as new device #%d\r\n", hTemp);
                            printf("Serial number=%d\r\n", lms[hTemp].SerialNumber);
                            printf("Devstatus=%08x\r\n", lms[hTemp].DevStatus);
                            printf("Model name=%s\r\n", lms[hTemp].ModelName);
                            printf("MinFrequency=%d\r\n", lms[hTemp].MinFrequency);
                            printf("MaxFrequency=%d\r\n", lms[hTemp].MaxFrequency);
                            printf("MaxPower=%d\r\n", lms[hTemp].MaxPower);
                            printf("MinPower=%d\r\n", lms[hTemp].MinPower);
                            printf("PowerScale=%d\r\n", lms[hTemp].PowerScale);
                            printf("Vendor ID=%04x\r\n", lms[hTemp].idVendor);
                            printf("Product ID=%04x\r\n", lms[hTemp].idProduct);
                            printf("Serial number=%s\r\n", lms[hTemp].Serialstr);
                        }
                    } else {
                        // our table of devices is full, not much we can do
                    }
                } /* if !bfound  */
                /* get any other data we might need */
            } /* else */
        } /* for dev */
    } /* for bus */
    
    //
    libusb_free_device_list(devlist, 1);        
    
    /* clean up the structure and mark unused slots */
    for (i = 1; i < MAXDEVICES; i++) {
        if ((lms[i].SerialNumber != 0) && !(lms[i].DevStatus & DEV_CONNECTED))
            lms[i].SerialNumber = 0;	// mark this slot as unused    

        if (lms[i].SerialNumber == 0)
            lms[i].DevStatus = 0;		// clear the status for robustness!
    } // end of zombie removal for loop
}

/* ----------------------------------------------------------------- */
//*****************************************************************************
//
// Function call for LMS Initialization
//  
//*****************************************************************************
void fnLMS_Init(void) {
    /* clear out the storage structure. Must be called once before anything else */
    int i;
    int status;
    int usb_status;

    for (i = 0; i < MAXDEVICES; i++) {
        lms[i].DevStatus = 0; // init to no devices connected
        lms[i].SerialNumber = 0; // clear the serial number
        lms[i].ModelName[0] = 0; // put a null string in each model name field
    }

    if (!Did_libusb_init){
        usb_status = libusb_init(&LMS_ctx);             // ensure we only call libusb_init once
        if (usb_status != 0){
            if (DEBUG_OUT > 0)  printf("libusb_init failed with usb_status = %d\r\n", usb_status);
            return;                 // not easy to recover from this...
        }
        Did_libusb_init = TRUE;
    }

    // check what timer resources are available
    if (clock_getres(CLOCK_MONOTONIC_COARSE, &mtimer_CTime )){
        if (DEBUG_OUT > 0)  printf("Unable to use HR timer, error number %d\r\n", errno);
        HasHRTimer = FALSE;
    }
    else {
        HasHRTimer = TRUE;
        if (DEBUG_OUT > 0)  printf("HR timer resolution = %ld in nanoseconds\r\n", mtimer_CTime.tv_nsec);
        clock_gettime(CLOCK_MONOTONIC_COARSE, &mtimer_LastSend);
    }

    if (DEBUG_OUT > 0) printf("library version %s\r\n", fnLMS_GetVersionStr());
}

//*****************************************************************************
//
// Function call to Set Test mode configuration
//  
//*****************************************************************************
void fnLMS_SetTestMode(bool testmode) {
    TestMode = testmode;
}

//*****************************************************************************
//
// Function call to find the number of connected LMS Devices
//  
//*****************************************************************************
int fnLMS_GetNumDevices() {
    int retval = 0;
    int NumDevices = 0;
    int i;

    // See how many devices we can find, or have found before
    if (TestMode) {

        // construct a fake LMS-103 device
        lms[1].SerialNumber = 100103;
        lms[1].DevStatus = lms[1].DevStatus | DEV_CONNECTED | PLL_LOCKED | FAST_PULSE_OPTION;
        lms[1].MinFrequency = 500000000; 		// 5Ghz in 10 hz resolution
        lms[1].MaxFrequency = 1000000000; 		// 10Ghz in 10 hz resolution
        lms[1].MaxPower = 40; 					// 10db in our .25db step system
        lms[1].MinPower = 40 - 200; 			// the LMS-103 has a 50 db attenuation range
        lms[1].PowerScale = 1; 					// the LMS-103 uses .25db units
        strcpy(lms[1].ModelName, "LMS-103");

        // construct a second fake LMS-123 device
        lms[2].SerialNumber = 200123;
        lms[2].DevStatus = lms[2].DevStatus | DEV_CONNECTED | PLL_LOCKED | FAST_PULSE_OPTION;
        lms[2].MinFrequency = 800000000;		// 8Ghz in 100Khz resolution
        lms[2].MaxFrequency = 1200000000;		// 12Ghz in 100Khz resolution
        lms[2].MaxPower = 40;					// 10db in our .25db step system
        lms[2].MinPower = 40 - 200;				// the '123 has a 50 db attenuation range
        lms[1].PowerScale = 1;
        strcpy(lms[2].ModelName, "LMS-123");

        retval = 2;

    } else {
        // go look for some real hardware
        FindVNXDevices();

        // Total up the number of devices we have
        for (i = 0; i < MAXDEVICES; i++) {
            if (lms[i].DevStatus & DEV_CONNECTED) NumDevices++;
        }
        retval = NumDevices;

    }
    return retval;
}

//*****************************************************************************
//
// Function call to read device information
//  
//*****************************************************************************
int fnLMS_GetDevInfo(DEVID * ActiveDevices) {
    int i;
    int NumDevices = 0;

    if (ActiveDevices == NULL) return 0; // bad array pointer, no place to put the DEVIDs

    for (i = 1; i < MAXDEVICES; i++) {	 // NB -- we never put an active device in lms[0] - so DEVIDs start at 1

        if (lms[i].DevStatus & DEV_CONNECTED) {
            ActiveDevices[NumDevices] = i;
            NumDevices++;
        }
    }

    return NumDevices;
}

//*****************************************************************************
//
// Function call to read Model Name
//  
//*****************************************************************************
int fnLMS_GetModelName(DEVID deviceID, char * ModelName) {
    int NumChars = 0;

    if (deviceID >= MAXDEVICES) {
        return 0;
    }

    NumChars = strlen(lms[deviceID].ModelName);
    // If NULL result pointer, just return the number of chars in the name
    if (ModelName == NULL) return NumChars;
    strcpy(ModelName, lms[deviceID].ModelName);

    return NumChars;
}

//*****************************************************************************
//
// Function call to open and initialize an LMS Device
//  
//*****************************************************************************
int fnLMS_InitDevice(DEVID deviceID) {

    LVSTATUS status;
    
    if ((deviceID >= MAXDEVICES) || (deviceID == 0)) {
        return INVALID_DEVID;
    }

    if (TestMode)
        lms[deviceID].DevStatus = lms[deviceID].DevStatus | DEV_OPENED;
    else {
        // Go ahead and open a handle to the hardware
        status = VNXOpenDevice(deviceID);

        if (status != STATUS_OK)  // VNXOpenDevice returns 0 (STATUS_OK) if the open succeeded, otherwise an error code
            return status;
  
        // Get the rest of the device parameters from the device
        if (DEBUG_OUT > 0) printf("Time to start getting parameters from device %d\r\n", deviceID);

        if (!GetFrequency(deviceID)) // read the frequency from the device (in 10Hz units)
            return BAD_HID_IO;

        if (DEBUG_OUT > 0) printf("Got Frequency...\r\n");

        if (!GetFStart(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got FStart...\r\n");

        if (!GetFEnd(deviceID)) // read the sweep end frequency
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got FEnd...\r\n");

        if (!GetSweep(deviceID)) // read the sweep time
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got Sweep...");

        if (!GetMaxPower(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got Max Power...\r\n");

        if (!GetPower(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got Power...\r\n");

        if (!GetRF_On(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got RF On...\r\n");

        if (!GetUseIntOsc(deviceID))
            return BAD_HID_IO;

        if (!GetUseExtSwpTrigger(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got External Sweep Trigger Status...\r\n");

        if (!GetFMinimum(deviceID))
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got FMinimum...\r\n");

        if (!GetPulseOffTime(deviceID))	// read the parameters for the internal pulse modulation feature
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got Pulse Off Time...\r\n");

        if (!GetPulseOnTime(deviceID))	// ditto
            return BAD_HID_IO;
        if (DEBUG_OUT > 0) printf("Got Pulse On Time...\r\n");

        if (!GetPulseMode(deviceID)) 	// ditto, put the pulse mode control bits into our Modebits dword, and update device status
            return BAD_HID_IO; 			// to show whether or not the Lab Brick has the fast pulse mode option

        // --- calculate the pulse on and off times in seconds from what we read ---
        if ((lms[deviceID].PulseOnTime & 0xF0000000) == 0x10000000) {
            lms[deviceID].PulseOnTimeSeconds = ((float)((lms[deviceID].PulseOnTime & 0x0FFFFFFF)) * 2.083333e-8);
            lms[deviceID].PulseOffTimeSeconds = ((float)((lms[deviceID].PulseOffTime & 0x0FFFFFFF)) * 2.083333e-8);

        } else {
            lms[deviceID].PulseOnTimeSeconds = ((float)((lms[deviceID].PulseOnTime & 0x0FFFFFFF)) * 1.00000e-6);
            lms[deviceID].PulseOffTimeSeconds = ((float)((lms[deviceID].PulseOffTime & 0x0FFFFFFF)) * 1.00000e-6);
        }

		if (lms[deviceID].HasChirp)
		{
			// for devices that support chirps we want to get the current settings
			if (!GetChirpLength(deviceID))
			{
				return BAD_HID_IO;
			}

			if (!GetChirpRepeatRate(deviceID))
			{
				return BAD_HID_IO;
			}
		}


        if (DEBUG_OUT > 0) printf("Device Open Successful!\r\n");

    } // end of real device open process case

    // if we got here everything worked OK
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to close the LMS Device connection
//  
//*****************************************************************************
int fnLMS_CloseDevice(DEVID deviceID) {

    int i;
    bool DevOpen = FALSE;
    int Trace_Save = 0;            // we save the global trace state and use Trace_Init for device init/de-init

    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (TestMode)
        lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_OPENED;
    else {

        // we use IO_Trace for the device init and close phases
        Trace_Save = Trace_Out;

        // Go ahead and close this hardware - the first step is to stop its read thread
        lms[deviceID].thread_command = THREAD_EXIT;

        // The thread handler will clear any pending reads on the device. We'll wait up to 1 second then give up.
        int retries;
        retries = 100;
        while (retries && (lms[deviceID].thread_command != THREAD_DEAD)) {
            catnap(10);  /* wait 10 ms */
            retries--;
        }
        if (DEBUG_OUT > 2) printf("After telling the thread to close, we have thread_command=%d retries=%d\r\n", lms[deviceID].thread_command, retries);
        lms[deviceID].thread_command = THREAD_EXIT; // this command should never be read ...

        libusb_close(lms[deviceID].DevHandle);  

        // Mark it closed in our list of devices
        lms[deviceID].DevStatus = lms[deviceID].DevStatus & ~DEV_OPENED;
    }

    // if no devices remain open, then we should be able to call libusb's exit procedure.
    for (i = 1; i<MAXDEVICES; i++){
        if (lms[i].DevStatus & DEV_OPENED) {
            DevOpen = TRUE;                             // we found an open lms device, not time to clean up yet
            break;
        }
    }

    if (!DevOpen){
        // we don't have any open lms devices, so we should be able to safely close     libusb. We will re-open it on the next GetNumDevices, or device open
        if (Did_libusb_init){
            libusb_exit(LMS_ctx);       // close our libusb context
            Did_libusb_init = FALSE;
        }
    }

    Trace_Out = Trace_Save;            // restore the normal trace level
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to read LMS Serial #
//  
//*****************************************************************************
int fnLMS_GetSerialNumber(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return 0;

    return lms[deviceID].SerialNumber;
}

//*****************************************************************************
//
// Function call to Set frequency the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SetFrequency(DEVID deviceID, int frequency) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_frequency = lms[deviceID].Frequency;

    if ((frequency >= lms[deviceID].MinFrequency) && (frequency <= lms[deviceID].MaxFrequency)) {
        lms[deviceID].Frequency = frequency;
        if (TestMode)
            return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else {
        return BAD_PARAMETER;
    }
    // the frequency value is OK, lets send it to the hardware
    char *ptr = (char *)&lms[deviceID].Frequency;

    if (DEBUG_OUT > 0) printf("deviceID = %d Frequency = %x\r\n", deviceID, *ptr);

    if (!SendReport(deviceID, VNX_DSS_FREQUENCY | VNX_SET, ptr, 4)) {

        lms[deviceID].Frequency = old_frequency;
        LockDev(deviceID, FALSE); // We're done using the frequency variables, unlock them..
        return BAD_HID_IO;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call Set start frequency to the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SetStartFrequency(DEVID deviceID, int startfrequency) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_startfrequency = lms[deviceID].StartFrequency;

    if ((startfrequency >= lms[deviceID].MinFrequency) && (startfrequency <= lms[deviceID].MaxFrequency)) {
        lms[deviceID].StartFrequency = startfrequency;
        if (TestMode)
            return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else
        return BAD_PARAMETER;

    // the sweep start frequency value is OK, lets send it to the hardware
    char *ptr = (char *)&lms[deviceID].StartFrequency;

    if (DEBUG_OUT > 0) printf("deviceID = %d StartFrequency = %x\r\n", deviceID, *ptr);
    if (!SendReport(deviceID, VNX_DSS_FSTART | VNX_SET, ptr, 4)) {
        lms[deviceID].StartFrequency = old_startfrequency;
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call Set End frequency to the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SetEndFrequency(DEVID deviceID, int endfrequency) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_endfrequency = lms[deviceID].EndFrequency;

    if ((endfrequency >= lms[deviceID].MinFrequency) && (endfrequency <= lms[deviceID].MaxFrequency)) {
        lms[deviceID].EndFrequency = endfrequency;
        if (TestMode)
            return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else
        return BAD_PARAMETER;

    // the sweep end frequency value is OK, lets send it to the hardware
    char *ptr = (char *)&lms[deviceID].EndFrequency;

    if (DEBUG_OUT > 0) printf("deviceID = %d SweepEndFrequency = %x\r\n", deviceID,  *ptr);

    if (!SendReport(deviceID, VNX_DSS_FSTOP | VNX_SET, ptr, 4)) {
        lms[deviceID].EndFrequency = old_endfrequency;
        return BAD_HID_IO;
    }
    return STATUS_OK;
}


//*****************************************************************************
//
// Function call Set frequency step to the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SetFrequencystep(DEVID deviceID, int freqstep) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_freqstep = lms[deviceID].FrequencyStep;

	lms[deviceID].FrequencyStep = freqstep;

    // the sweep end frequency value is OK, lets send it to the hardware
    char *ptr = (char *)&lms[deviceID].FrequencyStep;

    if (DEBUG_OUT > 0) printf("deviceID = %d frequencystep = %x\r\n", deviceID,  *ptr);

    if (!SendReport(deviceID, VNX_FSTEP | VNX_SET, ptr, 4)) {
        lms[deviceID].FrequencyStep = old_freqstep;
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Sweep Time
//  
//*****************************************************************************
LVSTATUS fnLMS_SetSweepTime(DEVID deviceID, int sweeptime) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_sweeptime = lms[deviceID].SweepTime;

    if (sweeptime >= VNX_MIN_SWEEPTIME) {
        lms[deviceID].SweepTime = sweeptime;
        if (TestMode)
            return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else
        return BAD_PARAMETER;

    // the sweep time value is OK, lets send it to the hardware
    char *ptr = (char *)&lms[deviceID].SweepTime;

    if (DEBUG_OUT > 0) printf("deviceID = %d SweepTime = %x\r\n", deviceID, *ptr);

    if (!SendReport(deviceID, VNX_DSS_SWPTIME | VNX_SET, ptr, 4)) {
        lms[deviceID].SweepTime = old_sweeptime;
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set output power level (absolute units)
//  
//*****************************************************************************
LVSTATUS fnLMS_SetPowerLevel(DEVID deviceID, int powerlevel) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    int old_powerlevel = lms[deviceID].Power;
    int ScaledAttenuation;

    // We use an absolute power level for the API, so the attenuation is computed from the value set by the user
    if (DEBUG_OUT > 1) printf("MaxPower = %d MinPower = %d \r\n", lms[deviceID].MaxPower, lms[deviceID].MinPower);

    // First off, check if the desired power setting is in range
    if ((powerlevel > lms[deviceID].MaxPower) || (powerlevel < lms[deviceID].MinPower))
        return BAD_PARAMETER; // power setting is out of range, bail out

    // Save the new power level setting
    lms[deviceID].Power = powerlevel;
    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    // We set the attenuation relative to MaxPower, so we have to subtract the power level value to get the attenuation
    // for power level settings below 0 db we end up adding the value since it is negative.

    powerlevel = (lms[deviceID].MaxPower - powerlevel);

    if ((powerlevel <= 0) || (powerlevel >= 4 * (lms[deviceID].LMSType == 15 ? MAX_ATTEN_LH : MAX_ATTEN)))
        return BAD_PARAMETER; // we should not see this case unless min or max power are mangled, but we're defending against
    // any possible bad internal attenuation command values

    // the power level value is OK, lets scale it and then send it to the hardware
    if (lms[deviceID].PowerScale) ScaledAttenuation = powerlevel / lms[deviceID].PowerScale; // defend against (unlikely) bad LMSPARAMS data
    else ScaledAttenuation = powerlevel;

    char *ptr = ( char *)&ScaledAttenuation;

    if (DEBUG_OUT > 0) printf("deviceID = %d PowerLevel = %x\r\n", deviceID, *ptr);
    if (!SendReport(deviceID, VNX_PWR | VNX_SET, ptr, 4)) {
        lms[deviceID].Power = old_powerlevel;
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set RF On/Off
//  
//*****************************************************************************
LVSTATUS fnLMS_SetRFOn(DEVID deviceID, bool on) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    char VNX_command[6] = {0, 0, 0, 0, 0, 0};

    if (on) {
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_RFON;
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_RFON;
        VNX_command[0] = 1;
    } else { // off
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_RFON;
        VNX_command[0] = 0;
    }

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (!SendReport(deviceID, VNX_RFMUTE | VNX_SET, VNX_command, 1)) {
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set pulse on time. In general use fnLMS_SetFastPulsedOutput
// 
//  
//*****************************************************************************
// *** revised in V1.01 to match the Windows API, pulseontime is now a floating point value
// This code converts from the floating point on time value (in seconds) to the ranged integer units
// it does not check for required relationship with off time.
LVSTATUS fnLMS_SetPulseOnTime(DEVID deviceID, float pulseontime) {
    int temp_pulseontime;

    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    float old_pulseontime = lms[deviceID].PulseOnTimeSeconds;

    if (pulseontime >= 0.10e-6 && pulseontime < 1000) { // pulse times can range from 100ns to 1000 seconds
        lms[deviceID].PulseOnTimeSeconds = pulseontime;
        if (TestMode)
            return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else
        return BAD_PARAMETER;

    if (pulseontime <= .001) // use the 48MHz clock
    {
        // generate the integer on time
        temp_pulseontime = (int)(48 * (pulseontime * 1.0e+6));
        temp_pulseontime |= PM48Mhz;
    } else {
        temp_pulseontime = (int)(pulseontime * 1.0e+6);
    }

    // the pulse on time value is OK (as far as we can tell...), lets send it to the hardware
    char *ptr = (char *)&temp_pulseontime;

    if (DEBUG_OUT > 0) printf("deviceID = %d Pulse On Time = %x\r\n", deviceID, *ptr);

    if (!SendReport(deviceID, VNX_ONTIME | VNX_SET, ptr, 4)) {
        lms[deviceID].PulseOnTime = old_pulseontime;
        return BAD_HID_IO;
    }

    lms[deviceID].PulseOnTime = temp_pulseontime; // we keep a copy of the encoded on time value in case we need it later
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set pulse off time. In general use fnLMS_SetFastPulsedOutput
//  
//*****************************************************************************
// *** revised in V1.01 to match the Windows API, pulseontime is now a floating point value
// This code converts from the floating point on time value (in seconds) to the ranged integer units
// it does not check for required relationship with on time - the calling application must ensure that the 
// on and off times will have the same encoding.
LVSTATUS fnLMS_SetPulseOffTime(DEVID deviceID, float pulseofftime) {
    int temp_pulseofftime;

    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    float old_pulseofftime = lms[deviceID].PulseOffTimeSeconds;

    // ------ make sure parameter is in range ----------
    if (pulseofftime >= 150.0e-9 && pulseofftime < 1000) {
        lms[deviceID].PulseOffTimeSeconds = pulseofftime;
        if (TestMode) return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    } else {
        return BAD_PARAMETER; // we end up here if the off time is less than 150ns or greater than 1000 sec
    }

    // find the encoding for the new pulse off time
    if (pulseofftime <= .001) // use the 48MHz clock
    {
        // generate the integer off time, it does not have the range flags!!
        temp_pulseofftime = (int)(48 * (pulseofftime * 1.0e+6));
    } else {
        temp_pulseofftime = (int)(pulseofftime * 1.0e+6);
    }

    // the pulse off time value is OK, lets send it to the hardware
    char *ptr = (char *)&temp_pulseofftime;

    if (DEBUG_OUT > 0) printf("deviceID = %d Pulse Off Time = %x\r\n", deviceID, *ptr);

    if (!SendReport(deviceID, VNX_OFFTIME | VNX_SET, ptr, 4)) {
        lms[deviceID].PulseOffTimeSeconds = old_pulseofftime;
        return BAD_HID_IO;
    }

    lms[deviceID].PulseOffTime = temp_pulseofftime; // we keep a copy of the encoded off time value in case we need it later
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to select internal or external pulse modulation control
//  This function turns the pulse modulation on or off
//*****************************************************************************
LVSTATUS fnLMS_EnableInternalPulseMod(DEVID deviceID, bool on) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    char VNX_command[6] = {0, 0, 0, 0, 0, 0};

    if (on)
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_PWMON;
    else
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_PWMON;

    VNX_command[0] = (lms[deviceID].Modebits & PWM_MASK) >> 8;

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (!SendReport(deviceID, VNX_PULSE_MODE | VNX_SET, VNX_command, 1)) {
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to enable external hardware control of the pulse modulation
//  
//*****************************************************************************
LVSTATUS fnLMS_SetUseExternalPulseMod(DEVID deviceID, bool external) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID)){
        return DEVICE_NOT_READY;
    }
    
    char VNX_command[6] = {0, 0, 0, 0, 0, 0};

    if (external)
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_EXTPWM;
    else
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_EXTPWM;

    VNX_command[0] = (lms[deviceID].Modebits & PWM_MASK) >> 8;

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (!SendReport(deviceID, VNX_PULSE_MODE | VNX_SET, VNX_command, 1)) {
        return BAD_HID_IO;
    }
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set output pulse modulation
//  
// Implemented in V1.01 to match the Windows API
// RD 6-6-24 improved calculation accuracy
//*****************************************************************************
LVSTATUS fnLMS_SetFastPulsedOutput(DEVID deviceID, float pulseontime, float pulsereptime, bool on) {
    int temp_pulseontime;
    int temp_pulseofftime;
	double reptimetest;
	double HP_pulseontime;	// promote the args to improve accuracy
	double HP_pulsereptime;
    char VNX_command[6] = {0, 0, 0, 0, 0, 0};
    
	if (deviceID >= MAXDEVICES)
		return INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return DEVICE_NOT_READY;
	
	if (!(lms[deviceID].DevStatus & FAST_PULSE_OPTION))
		return BAD_PARAMETER;

    float old_pulseontime = lms[deviceID].PulseOnTimeSeconds;
    float old_pulseofftime = lms[deviceID].PulseOffTimeSeconds;

    if (pulsereptime <= pulseontime) // the on time has to be less than the repetition time
        return BAD_PARAMETER;

    // ------ first we have to convert from the floating point times to our integers ---------------------
    // we have to pick the range of units depending on whether or not we can use the 48MHz, 1 MHz or 1ms clocks
    if (DEBUG_OUT > 0) printf("pulseontime = %f, pulsereptime = %f\r\n", pulseontime, pulsereptime);
	
	HP_pulseontime = (double)pulseontime;
	HP_pulsereptime = (double)pulsereptime;

	reptimetest = 1.041688E-3;

    if (HP_pulsereptime < reptimetest) 						// use the 48MHz clock
    {
        if ((HP_pulseontime + 5.0e-9 > HP_pulsereptime)) {	// the on time plus one clock has to be less than the repetition time
            return BAD_PARAMETER;
        }

        // generate the integer on time
        if (DEBUG_OUT > 1) printf("using 48MHz clock\r\n");
        temp_pulseontime = (int) round(48 * (HP_pulseontime * 1.0e+6));
        temp_pulseofftime = (int) round(48 * (HP_pulsereptime * 1.0e+6)) - temp_pulseontime;

        if (DEBUG_OUT > 1) printf(" 48MHZ temp_pulseontime = %d, temp_pulseofftime = %d\r\n", temp_pulseontime, temp_pulseofftime);
		
		temp_pulseontime |= PM48Mhz; // add our units encoding marker
        if (temp_pulseofftime <= 0) return BAD_PARAMETER;

    } else if (HP_pulsereptime > .001 && HP_pulsereptime <= .050) {	// use the 1MHz clock
        if ((HP_pulseontime + 1.0e-6 > HP_pulsereptime)) { 			// the on time plus one clock has to be less than the repetition time
            return BAD_PARAMETER;
        }

        if (DEBUG_OUT > 1) printf("using 1MHz clock\r\n");

        temp_pulseontime = (int) round(HP_pulseontime * 1.0e+6);
        temp_pulseofftime = (int) round(HP_pulsereptime * 1.0e+6) - temp_pulseontime;

        if (temp_pulseofftime <= 0) return BAD_PARAMETER;
        if (DEBUG_OUT > 0) printf(" 1MHZ temp_pulseontime = %d, temp_pulseofftime = %d\r\n", temp_pulseontime, temp_pulseofftime);
    } else  { 
 
		// for rep time > 50 ms we use the software timer (its really the same as the above case)
        if ((HP_pulseontime + .001 >= HP_pulsereptime)) { // the on time plus one clock has to be less than the repetition time
            return BAD_PARAMETER;
        }

        if (HP_pulsereptime > 1000) return BAD_PARAMETER; // maximum time allowed by API is 1000 seconds
        if (DEBUG_OUT > 1) printf("using 1 ms. timer\r\n");

        // ---- we represent the time in 1 microsecond units --------
        temp_pulseontime = (int) round(HP_pulseontime * 1.0e+6);
        temp_pulseofftime = (int) round(HP_pulsereptime * 1.0e+6) - temp_pulseontime;

        if (DEBUG_OUT > 1) printf(" SW temp_pulseontime = %d, temp_pulseofftime = %d\r\n", temp_pulseontime, temp_pulseofftime);
        if (temp_pulseofftime <= 0) return BAD_PARAMETER;
    }

    // At this point we can update our local copies of the on and off time in seconds
    // We'll restore the old values if somehow the hardware I/O fails
    lms[deviceID].PulseOnTimeSeconds = pulseontime;
    lms[deviceID].PulseOffTimeSeconds = pulsereptime - pulseontime;

    if (DEBUG_OUT > 1) printf(" Setting PulseOnTimeSeconds = %f, PulseOffTimeSeconds = %f\r\n", lms[deviceID].PulseOnTimeSeconds, lms[deviceID].PulseOffTimeSeconds);

    // Now send the parameters to the device if we aren't in test mode
    if (TestMode) {
        if (on) { // keep the mode bits in sync
            lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_PWMON;
        } else {
            lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_PWMON;
        }
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW
    }

    // First we disable any active fast pulse mode operation, including external mode ...
    VNX_command[0] = 0; // pulse mode off, internal pulse modulation selected
    if (!SendReport(deviceID, VNX_PULSE_MODE | VNX_SET, VNX_command, 1)) {
        // -- our IO failed, so leave the old settings and bail out --
        //    we can't do much about restoring the pulse mode state...
        lms[deviceID].PulseOnTimeSeconds = old_pulseontime;
        lms[deviceID].PulseOffTimeSeconds = old_pulseofftime;
        return BAD_HID_IO;
    }

    // Then we send the on time, followed by the off time
    char *ptr = (char * )&temp_pulseontime;

    if (DEBUG_OUT > 0) printf("deviceID = 0x%x Pulse On Time LSByte = 0x%x PulseOnTime = %d\r\n", deviceID, *ptr, temp_pulseontime);

    if (!SendReport(deviceID, VNX_ONTIME | VNX_SET, ptr, 4)) {

        // -- our IO failed, so leave the old settings and bail out --
        lms[deviceID].PulseOnTimeSeconds = old_pulseontime;
        lms[deviceID].PulseOffTimeSeconds = old_pulseofftime;
        return BAD_HID_IO;
    }

    ptr = (char * )&temp_pulseofftime;
    if (DEBUG_OUT > 0) printf("deviceID = 0x%x Pulse Off Time LSByte = 0x%x PulseOffTime = %d\r\n", deviceID, *ptr, temp_pulseofftime);

    if (!SendReport(deviceID, VNX_OFFTIME | VNX_SET, ptr, 4)) {

        // -- we're in a pickle here, we set the new pulse on time, but failed on the new off time setting
        //    so our state variables may not be viable value wise, but since talking to the device is failing
        //    we really can't do much about it!
        lms[deviceID].PulseOffTimeSeconds = old_pulseofftime;
        return BAD_HID_IO;
    }

    // -- time to activate the pulse mode operation with the new settings --
    if (on) {
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_PWMON;
    } else {
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_PWMON;
    }

    VNX_command[0] = (lms[deviceID].Modebits & PWM_MASK) >> 8;

    if (!SendReport(deviceID, VNX_PULSE_MODE | VNX_SET, VNX_command, 1)) {

        // -- a failure here leaves the settings intact, and in sync, except for the mode bits
        //    probably not worth worrying about trying to restore them since who knows what is
        //    going on below us to cause the failure...
        return BAD_HID_IO;
    }

    if (DEBUG_OUT > 0) printf("Pulse Mode Set to: 0x%x\n", VNX_command[0]);

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call select whether or not to use the internal frequency reference
//  
//*****************************************************************************
LVSTATUS fnLMS_SetUseInternalRef(DEVID deviceID, bool internal) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID)){
        return DEVICE_NOT_READY;
    }
    
    char VNX_command[6] = {0, 0, 0, 0, 0, 0};

    if (internal) {
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_INTREF;
        VNX_command[0] = 1;
    } else {
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_INTREF;
        VNX_command[0] = 0;
    }

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (!SendReport(deviceID, VNX_INTOSC | VNX_SET, VNX_command, 1)) {
        return BAD_HID_IO;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to enable the external hardware sweep trigger
//  
//*****************************************************************************
LVSTATUS fnLMS_SetUseExtSweepTrigger(DEVID deviceID, bool external) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID)){
        return DEVICE_NOT_READY;
    }
    
    char VNX_command[6] = {0, 0, 0, 0, 0, 0};

    if (external) {
        lms[deviceID].Modebits = lms[deviceID].Modebits | MODE_EXTSWEEP;
        VNX_command[0] = 1;
    } else {
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~MODE_EXTSWEEP;
        VNX_command[0] = 0;
    }

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (!SendReport(deviceID, VNX_EXTSWP | VNX_SET, VNX_command, 1)) {
        return BAD_HID_IO;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set the sweep direction, True for upwards, False for downwards
// this parameter should match the sweep start and end frequencies
//  
//*****************************************************************************
LVSTATUS fnLMS_SetSweepDirection(DEVID deviceID, bool up) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;
    if (up)
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_DIRECTION; // sweep direction up (bit == 0)
    else
        lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_DIRECTION; // sweep direction downwards

    return STATUS_OK;
}


//*****************************************************************************
//
// Function call to enable bidirectional sweeps 
// bidir = TRUE for bidirectional sweeps
// bidir = FALSE for unidirectional sweeps
//*****************************************************************************
LVSTATUS fnLMS_SetSweepbidirection(DEVID deviceID, bool bidir) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;
	
    if (bidir)
		lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_BIDIR;	// sweeps return to FStart
    else
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_BIDIR;	// sweeps end at FStop

    return STATUS_OK;
}

//*****************************************************************************
//
// Set the sweep mode to enable chirping -- mode = TRUE for chirps, FALSE for ordinary sweeps
//  
//*****************************************************************************
LVSTATUS fnLMS_SetChirpMode(DEVID deviceID, bool mode) {
	if (deviceID >= MAXDEVICES)
		return INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return DEVICE_NOT_READY;

	if (mode) {
		// make sure our hardware supports chirping
		if (!(lms[deviceID].HasChirp)) return BAD_PARAMETER;

		lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_CHIRP;	// Enable chirping
	}
	else {
		lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_CHIRP;	// Enable normal sweeps
	}

	return STATUS_OK;
}

// Set the length of the chirp. The length is specified in seconds
LVSTATUS fnLMS_SetChirpLength(DEVID deviceID, float chirplength) {
	int m_ChirpLength;

	if (deviceID >= MAXDEVICES)
		return INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return DEVICE_NOT_READY;

	int old_chirplength = lms[deviceID].ChirpLength;

	m_ChirpLength = (int)(chirplength * 48000000);		// convert our API value of seconds to the number of 48 MHz clocks we use internally

	// printf("m_ChirpLength (decimal in 48MHz units) = %d\n", m_ChirpLength);		// !! RD Debug !!

	if (m_ChirpLength >= VNX_MIN_CHIRPLENGTH) {

		lms[deviceID].ChirpLength = m_ChirpLength;
		if (TestMode) {
			return STATUS_OK;		// in test mode we update our internal variables, but don't talk to the real HW
		}
	}
	else {
		return BAD_PARAMETER;
	}

	// we can't easily validate the chirp length since it depends on the start and end frequencies which may or may not be set yet
	// so we just ensure it is in the realm of the possible and send it along to the hardware
	m_ChirpLength |= PM48Mhz;

	char *ptr = (char *)&m_ChirpLength;

	//	printf("hDevice = %x ptr = %x ChirpLength = %x\n", lms[deviceID].hDevice, ptr, *ptr); 	// Debug

	if (!SendReport(deviceID, VNX_CHIRP_LENGTH | VNX_SET, ptr, 4)) {

		lms[deviceID].ChirpLength = old_chirplength;
		return BAD_HID_IO;
	}

	return STATUS_OK;
}

// Set the repeat time of the chirp. The length is specified in seconds
LVSTATUS fnLMS_SetChirpRepeatRate(DEVID deviceID, float chirp_repeat) {
	int m_ChirpRepeat;

	if (deviceID >= MAXDEVICES)
		return INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return DEVICE_NOT_READY;

	int old_chirp_repeat = lms[deviceID].ChirpRepeatRate;

	m_ChirpRepeat = (int)(chirp_repeat * 1000000);		// convert our API value of seconds to the number of 1 MHz clocks we use internally

	// we can't easily validate the chirp repeat rate since it depends on the chirp length which may or may not be set yet
	// so we just ensure it is in the realm of the possible and send it along to the hardware

	if (m_ChirpRepeat >= 10 && m_ChirpRepeat < 16777214) {	// our minimum repeat time is 10 microseconds, maximum is 16,777,214 microseconds (24 bit counter)

		lms[deviceID].ChirpLength = m_ChirpRepeat;
		if (TestMode) {
			return STATUS_OK;		// in test mode we update our internal variables, but don't talk to the real HW
		}
	}
	else {
		return BAD_PARAMETER;
	}

	char *ptr = (char *)&m_ChirpRepeat;

	//	printf("hDevice = %x ptr = %x ChirpRepeatTime = %x\n", lms[deviceID].hDevice, ptr, *ptr);

	if (!SendReport(deviceID, VNX_CHIRP_RATE | VNX_SET, ptr, 4)) {

		lms[deviceID].ChirpRepeatRate = old_chirp_repeat;
		return BAD_HID_IO;
	}

	return STATUS_OK;
}

//*****************************************************************************
//
// Function call to set sweep mode, True for repeated, False for one time
//  
//*****************************************************************************
LVSTATUS fnLMS_SetSweepMode(DEVID deviceID, bool mode) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (mode) {
        lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_CONTINUOUS;	// Repeated sweep
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_ONCE;
    } else {
        lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_ONCE;			// one time sweep
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_CONTINUOUS;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// This function call also sets whether sweeps are unidirectional (sawtooth shaped)
// or bidirectional (triangle shaped)
// swptype = TRUE for bidirectional sweeps
// swptype = FALSE for unidirectional sweeps
//*****************************************************************************
LVSTATUS fnLMS_SetSweepType(DEVID deviceID, bool swptype) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (swptype)
        lms[deviceID].Modebits = lms[deviceID].Modebits | SWP_BIDIR; // sweep bidirectionally 
    else
        lms[deviceID].Modebits = lms[deviceID].Modebits & ~SWP_BIDIR; // sawtooth shaped sweep envelope

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to start a sweep
//  
//*****************************************************************************
LVSTATUS fnLMS_StartSweep(DEVID deviceID, bool go) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    char VNX_sweep[4] = {0, 0, 0, 0};

    if (go)
        VNX_sweep[0] = (char) lms[deviceID].Modebits & MODE_SWEEP;
    else
        VNX_sweep[0] = 0;

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    if (DEBUG_OUT > 0) printf(" sending a sweep command = %x\r\n", VNX_sweep[0]);

    if (!SendReport(deviceID, VNX_SWEEP | VNX_SET, VNX_sweep, 1)) {
        return BAD_HID_IO;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to save the settings in the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SaveSettings(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (TestMode)
        return STATUS_OK; // in test mode we update our internal variables, but don't talk to the real HW

    char VNX_savesettings[3] = {
        0x42,
        0x55,
        0x31
    }; //three byte key to unlock the user protection.

    if (!SendReport(deviceID, VNX_SAVEPAR | VNX_SET, VNX_savesettings, 3)) {
        return BAD_HID_IO;
    }

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to get the current frequency in 10Hz units
//  
//*****************************************************************************
int fnLMS_GetFrequency(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].Frequency;
}

//*****************************************************************************
//
// Function call to get the sweep start frequency in 10Hz units
//  
//*****************************************************************************
int fnLMS_GetStartFrequency(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].StartFrequency;
}

//*****************************************************************************
//
// Function call to get the sweep end frequency in 10Hz units
//  
//*****************************************************************************
int fnLMS_GetEndFrequency(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].EndFrequency;
}

// Get frequency step
int fnLMS_GetFrequencystep(DEVID deviceID){
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].FrequencyStep;
}
//*****************************************************************************
//
// Function call to get the sweep time in 1ms units
//  
//*****************************************************************************
int fnLMS_GetSweepTime(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].SweepTime;
}

//*****************************************************************************
//
// Function call to find out if the LMS device is muted
//  
//*****************************************************************************
int fnLMS_GetRF_On(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].Modebits & MODE_RFON)
        return 1;
    else
        return 0;
}

//*****************************************************************************
//
// Function call to Set Profile Dwell Time to the device
//  
//*****************************************************************************
int fnLMS_GetUseInternalRef(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].Modebits & MODE_INTREF)
        return 1;
    else
        return 0;
}

//*****************************************************************************
//
// Function call to find if the external sweep trigger is enabled
//  
//*****************************************************************************
int fnLMS_GetUseExtSweepTrigger(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].Modebits & MODE_EXTSWEEP)
        return 1;
    else
        return 0;
}

//*****************************************************************************
//
// Function call to get the output power level in 0.25 db units
//  
//*****************************************************************************
int fnLMS_GetPowerLevel(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].Power;
}

//*****************************************************************************
//
// Function call to get the maximum output power level in 0.25 db units
//  
//*****************************************************************************
int fnLMS_GetMaxPwr(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].MaxPower;
}

//*****************************************************************************
//
// Function call to get the minimum output power level in 0.25 db units
//  
//*****************************************************************************
int fnLMS_GetMinPwr(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].MinPower;
}

//*****************************************************************************
//
// Function call to get the maximum operating frequency in 10 Hz units
//  
//*****************************************************************************
int fnLMS_GetMaxFreq(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    return lms[deviceID].MaxFrequency;
}

//*****************************************************************************
//
// Function call to get the minimum operating frequency in 10 Hz units
//  
//*****************************************************************************
int fnLMS_GetMinFreq(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    return lms[deviceID].MinFrequency;
}


//*****************************************************************************
//
// Functions to get chirp related parameters
//  
//*****************************************************************************
float fnLMS_GetChirpLength(DEVID deviceID) {
	float ChirpLength;

	if (deviceID >= MAXDEVICES)
		return F_INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return F_DEVICE_NOT_READY;

	if (lms[deviceID].HasChirp) {
		ChirpLength = (float)(lms[deviceID].ChirpLength & 0x0FFFFFFF) / 48.0e+6;	// Internally we use encoded 48MHz units, we convert to floating point seconds.
		return ChirpLength;
	}
	else{
		return F_BAD_PARAMETER;
	}
}

float fnLMS_GetChirpRepeatRate(DEVID deviceID) {
	if (deviceID >= MAXDEVICES)
		return F_INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return F_DEVICE_NOT_READY;

	if (lms[deviceID].HasChirp) {
		return (float) (lms[deviceID].ChirpRepeatRate) * 1.0e-6;	// our internal variable is the number of 1 microsecond clocks, the API is in seconds
	}
	else{
		return F_BAD_PARAMETER;
	}
}

// Find out if the device supports chirping
int fnLMS_GetHasChirpMode(DEVID deviceID) {
	if (deviceID >= MAXDEVICES)
		return INVALID_DEVID;

	if (!CheckDeviceOpen(deviceID))
		return DEVICE_NOT_READY;

	if (lms[deviceID].HasChirp) {
		return 1;
	}
	else{
		return 0;
	}
}


//*****************************************************************************
//
// Function call to get the on time for the pulse, in seconds
//  (Changed in V1.01 to match the Windows API)
//*****************************************************************************
float fnLMS_GetPulseOnTime(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return F_INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return F_DEVICE_NOT_READY;

    return lms[deviceID].PulseOnTimeSeconds;
}

//*****************************************************************************
//
// Function call to get the off time for the pulse, in seconds
//  (Changed in V1.01 to match the Windows API)  
//*****************************************************************************
float fnLMS_GetPulseOffTime(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return F_INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return F_DEVICE_NOT_READY;

    return lms[deviceID].PulseOffTimeSeconds;
}

//*****************************************************************************
//
// Function call to get the pulse modulation mode (on/off)
//  
//*****************************************************************************
int fnLMS_GetPulseMode(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].Modebits & MODE_PWMON)
        return 1;
    else
        return 0;
}

//*****************************************************************************
//
// Function call to find if the internal pulse modulation source is being used
//  
//*****************************************************************************
int fnLMS_GetUseInternalPulseMod(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].Modebits & MODE_EXTPWM)
        return 0; 	// Note -- the flag is true for external PWM source, in the DLL API we 
					//         report use of the internal source as true.
    else
        return 1;
}

//*****************************************************************************
//
// Function call to find if bidirectional sweep mode is enabled
// RD 6/2024 revised for consistency
//*****************************************************************************
int fnLMS_GetSweepbidirectionalmode(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].DevStatus & SWP_BIDIRECTIONAL)
        return 1;
    else
        return 0;
}

//*****************************************************************************
//
// Function call to get the PLL Lock status
//  
//*****************************************************************************
int fnLMS_GetPLLLock(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].DevStatus & PLL_LOCKED)
        return 1; // pll lock status
    else
        return 0;
	
}

//*****************************************************************************
//
// Function call to get the overall device status
//  
//*****************************************************************************
int fnLMS_GetDeviceStatus(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    return lms[deviceID].DevStatus;
}

//*****************************************************************************
//
// Function call to get Sweep mode from the device
//  
//*****************************************************************************
int fnLMS_GetSweepmode(DEVID deviceID) {
    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    if (lms[deviceID].DevStatus & SWP_REPEAT)
        return 2; // Repeat
    else
        return 1;
	
}

//*****************************************************************************
//
// Function call to send raw data message to the device
//  
//*****************************************************************************
LVSTATUS fnLMS_SendRawmsg_USBHID(DEVID deviceID, char *buff) {
    int command, cmdlength;
    char VNX_param[6] = {0, 0, 0, 0, 0, 0};

    if (deviceID >= MAXDEVICES)
        return INVALID_DEVID;

    if (!CheckDeviceOpen(deviceID))
        return DEVICE_NOT_READY;

    // Read Command Byte
    command = buff[0];

    // Read Data length;
    cmdlength = buff[1];

    // Max data length is 6 bytes
    if(cmdlength > 6)
        return BAD_HID_IO;

    // Check the command ID for get /set message
    if(command & VNX_SET)  // Write Command Message
    {
        if (!SendReport(deviceID, command, &buff[2], cmdlength)) {
            return BAD_HID_IO;
        }
    }
    else  // Read Command Message
    {
        if (!SendReport(deviceID, (char)command, VNX_param, 0)) {
            return BAD_HID_IO;
        }
    }
    return STATUS_OK;
}
