// -------------------------------------------------------------------------------------------------------------------------
// Module Name: LDA Ethernet Socket Communication Device Control Linux Library
// JA 01/29/2020    Initial Verison oF Ethernet Library
// RD 05/03/2024    Updates, fixes, new mc functions etc. -------------------------------------------------------------------------------------------------------------------------
// This library uses .05db units for attenuation values, so 10db is represented by 200. (multiply by 20 to convert db to api units)
/// ---------- Include headers ----------------
#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 <sys/socket.h>
#include <arpa/inet.h> 
#include <netdb.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/timerfd.h>
#include <sys/wait.h> 
#include "ldasocket.h"

/// ---------- Macros ----------------
#define LDA_SOCKET_PORT  "40001"
#define FALSE 0
#define TRUE 1          
#define PACKET_CTRL_LEN 8
#define PACKET_INT_LEN  8

#define LIBVER "0.0.6"
#define LDA_DLLVERSION 0x006        // we return an integer representation of the version with one byte each for major and minor version

// to force LDA library debugging messages choose the following definitions
#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 */

// IP Address Validator
#define DELIM "."

// Thread States Macros
#define THREAD_IDLE 0
#define THREAD_START 1
#define THREAD_EXIT 3
#define THREAD_DEAD 4
#define THREAD_ERROR -1

/* -----------------------------------------------------------------
Global Data to maintain the Devices data & states for each process
-------------------------------------------------------------------*/
bool TestMode = FALSE;          // if TestMode is true we fake it -- no HW access
int  ldadevcount = 0;
time_t starttime;
LDAPARAMS lda [MAXDEVICES+1];      // an array of structures each of which holds the info for a given device

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


/// ---------- Const Defintions----------
// product names
const char *sVNX_devicenames[] ={"LDA-102" , "LDA-602", "LDA-302P-H", "LDA-302P-1", "LDA-302P-2", "LDA-102-75",
                                  "LDA-102E", "LDA-602E", "LDA-183", "LDA-203", "LDA-102EH", "LDA-602EH", 
                                  "LDA-602Q", "LDA-906V", "LDA-133", "LDA-5018", "LDA-5040", "LDA-906V-8",
                                  "LDA-802EH", "LDA-802Q", "LDA-802-8", "LDA-906V-4"};

char STATUS_QUERY_ID[]= {VNX_GETSERNUM, VNX_MAXCHANNEL, VNX_MODELNAME, VNX_SWVERSION, VNX_IPMODE, VNX_IPADDR, VNX_IPMASK,
                         VNX_IPGW, VNX_MINFREQUENCY, VNX_MAXFREQUENCY,
                         VNX_MINATTEN, VNX_MAXATTEN, VNX_CHANNEL, VNX_FREQUENCY, VNX_PWR, VNX_ASTART, VNX_ASTOP,
                         VNX_ASTEP, VNX_ASTEP2, VNX_ADWELL, VNX_ADWELL2, VNX_AIDLE, VNX_AHOLD,
                         VNX_RFMUTE, VNX_PROFILECOUNT, VNX_PROFILEDWELL, VNX_PROFILEIDLE, VNX_SWEEP};

char LibVersion[] = LIBVER;

static void *rxdatahandler_callback (void *ptr);

/// ----------  Static Function Calls ----------------
//***************************************************************************
//
// Function call for returning the LibVersion
//
//*****************************************************************************
char* fnLDA_LibVersion(void) {
    return LibVersion;
}

//***************************************************************************
//
// Function call for returning the DLL Library Version
//
//*****************************************************************************
int fnLDA_GetLibVersion(void){
    return LDA_DLLVERSION;
}

//***************************************************************************
//
// Function call for Setting test mode
//
//*****************************************************************************
void fnLDA_SetTestMode(bool testmode) {
    TestMode = testmode;
}


//***************************************************************************
//
// Function call for delay loop
//
//*****************************************************************************
/* 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);
}


//***************************************************************************
//
// Validate netconfig digits and return 1 if string contain only digits, else return 0
//
//*****************************************************************************
static int valid_digit(char *ip_str){
    while (*ip_str) {
        if (*ip_str >= '0' && *ip_str <= '9')
            ++ip_str;
        else
            return 0;
    }
    return 1;
}

//***************************************************************************
//
// Validate IP Configuration -return 1 if IP string is valid, else return 0
//
//*****************************************************************************
static int is_valid_ip(char *ip_str){
    int num, dots = 0;
    char *ptr;
    char lstr[16];
    strcpy(lstr,ip_str);

    if (lstr == NULL)
        return 0;

    ptr = strtok(lstr, DELIM);

    if (ptr == NULL)
        return 0;

    while (ptr) {

        /* after parsing string, it must contain only digits */
        if (!valid_digit(ptr))
            return 0;

        num = atoi(ptr);

        /* check for valid IP */
        if (num >= 0 && num <= 255) {
            /* parse remaining string */
            ptr = strtok(NULL, DELIM);
            if (ptr != NULL)
                ++dots;
        } else
            return 0;
    }

    /* valid IP string must contain 3 dots */
    if (dots != 3)
        return 0;
    return 1;
}

//***************************************************************************
//
// Function call to Get DeviceID based on the deviceip from the device entry list
//
//*****************************************************************************
static int GetDeviceID(char* deviceip){
    int i;

    // Loop through to get the device index
	// the array has one extra element since we start active HW devices at lda[1] 
    for(i = 1; i < MAXDEVICES+1; i++)
    {
        if(strcmp(lda[i].deviceip, deviceip) == 0)
            return i;
    }

    return -1;
}

//***************************************************************************
//
// Function call to check whether lda device socket is open or not
//
//*****************************************************************************
static bool isDeviceSockOpen(char* deviceip) {
  int devid;
  if (TestMode) return TRUE;		// in test mode all devices are always available
  devid = GetDeviceID(deviceip);

  if (DEBUG_OUT > 0) printf("devid:%d\n",devid);

  if(devid > 0)
  {
      if (DEBUG_OUT > 1) printf(" In isDeviceSockOpen devicesockfd:%d\n",lda[devid].devicesockfd);

      // Even for multichannel devices, we can use channel 0 to know if the device is open
      if (lda[devid].devicesockfd != 0)
        return TRUE;
      else
        return FALSE;
  }
  else
    return FALSE;
}
//***************************************************************************
//
// Function call to Open the Device Socket Connection
//
//*****************************************************************************
static int vnxDeviceOpen(char* deviceip) {
    int sockfd;
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICSERV;

    if (DEBUG_OUT > 1) printf("IP:%s\n",deviceip);
 
    // Convert IPv4 and IPv6 addresses from text to binary form 
    if(inet_pton(AF_INET, deviceip, &hints.ai_addr)<=0)  
    { 
        if (DEBUG_OUT > 0) printf("\nInvalid address/ Address not supported \n"); 
        return -1; 
    } 

    // Check if Device is already open or not
    if (getaddrinfo(deviceip, LDA_SOCKET_PORT, &hints, &result) != 0)
        return -1;

    for (rp = result; rp != NULL; rp = rp->ai_next)
    {
        //  Open the Socket Connection 
        sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (sockfd == -1)
            continue;

        // Connect to the remote Server on Socket
        if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;

        // Close socket file descriptor if not connecting
        close(sockfd);
    }

    if (rp == NULL)
        return -1;

    freeaddrinfo(result);

    return sockfd;
}

//***************************************************************************
//
// Function call to Send the VNX commands through the Socket to the given ip
//  device. This function waits for the response from get commands
//*****************************************************************************
static bool SendReport(int deviceID, char command, char *pBuffer, int cbBuffer)
{
  int index;
  int timedout;

  // 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 (index=0; index<HID_REPORT_LENGTH; index++)
      lda[deviceID].sndbuff[index] = 0;

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

  if (DEBUG_OUT > 1) {
    printf("SR to device %d: ", deviceID);
    for (index=0; index<8; index++) {
      printf("%02x ", (uint8_t)lda[deviceID].sndbuff[index]);
    }
    printf("\r\n");
  }

  lda[deviceID].responseready = 0; // Clear Device Response before command send

  // Call Socket Function to send packet data to the device
  if (write(lda[deviceID].devicesockfd, lda[deviceID].sndbuff, PACKET_CTRL_LEN) < 0 )
    {
        if (DEBUG_OUT > 0) printf("sending Message Error!");
    }

  //Check for Get/Set Message type and only wait for response to Get messages
  if((command &  VNX_SET) == 0)
  {
	  // --- then we wait for the read thread's parser to signal that it got the response
	  starttime = time(NULL);
	  timedout = 0;
	  
	  // wait until the value is decoded or 1 second has gone by
	  // RD 4-2019 modified to yield the thread during the wait
	  while ((!lda[deviceID].responseready) && (0 == timedout)) {
	    catnap(10);   // yield for 10 ms, speed matters here
	    if ((time(NULL)-starttime) > 1)
		{
			if (DEBUG_OUT > 0) printf("Timeout waiting for response to %x command", command);
			timedout = 1;
		}
	  }

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


//***************************************************************************
//
// Function call to clear caches in the device data structures with default data
//  
//*****************************************************************************
static void ClearDevCache(DEVID deviceID, int num_channels) {
    int chnl;
    int index;

    if (num_channels > CHANNEL_MAX) num_channels = CHANNEL_MAX; // protect our arrays

    lda[deviceID].MinFrequency = 200;
    lda[deviceID].MaxFrequency = 8000;

    for (chnl = 0; chnl< num_channels; chnl++) {
        lda[deviceID].Attenuation[chnl] = -1;           // we mark the attenuation as unknown to start
        lda[deviceID].WorkingFrequency[chnl] = -1;  	// and everybody else...
        lda[deviceID].RampStart[chnl] = -1;
        lda[deviceID].RampStop[chnl] = -1;
        lda[deviceID].AttenuationStep[chnl] = -1;
        lda[deviceID].AttenuationStep2[chnl] = -1;
        lda[deviceID].DwellTime[chnl] = -1;
        lda[deviceID].DwellTime2[chnl] = -1;
        lda[deviceID].IdleTime[chnl] = -1;
        lda[deviceID].HoldTime[chnl] = -1;
        lda[deviceID].Modebits[chnl] = 0;               // Modebits aren't directly readable, but clearing them here for convenience
        lda[deviceID].ProfileDwellTime[chnl] = -1;
        lda[deviceID].ProfileIdleTime[chnl] = -1;
        lda[deviceID].ProfileCount[chnl] = -1;
    
        // clear the profile cache
        for (index = 0; index < PROFILE_MAX_RAM; index++) {
            lda[deviceID].Profile[chnl][index] = PROFILE_EMPTY;     // set all the profile elements to PROFILE_EMPTY
                                                                // Note - we only use the first 50 for HiRes devices, and the first 100 for legacy devices
        }
    }

}

/*******************************************************************************
 * \brief    Find Position of the bit set for channel mask identification
 *
 ******************************************************************************/
 // A utility function to check whether n is a power of 2 or not. 
static int isPowerOfTwo(unsigned n) 
{ 
    return n && (!(n & (n - 1))); 
} 
static int findPosition(unsigned n) 
{ 
    if (!isPowerOfTwo(n)) 
        return -1; 
  
    unsigned i = 1, pos = 1; 
  
    // Iterate through bits of n till we find a set bit 
    // i&n will be non-zero only when 'i' and 'n' have a set bit 
    // at same position 
    while (!(i & n)) { 
        // Unset current bit and set the next bit in 'i' 
        i = i << 1; 
  
        // increment position 
        ++pos; 
    } 
  
    return pos; 
} 


/*******************************************************************************
 * \brief    Parse Data Frame received from the socket
 *
 ******************************************************************************/
static int parseDataFrame(char* msg, int count, int sockfd, int devid)
{
    int i;
    int chnl;
    RESPONSE_DATA_T dataresp;
    unsigned short dataval_16;
	unsigned short prfindex;
    unsigned int dataval_32;
    unsigned char dataval_8;
	char *dataptr;
	char namebuffer[7];

#if 0
    for(i=0; i < count; i++)
    {
        if (DEBUG_OUT > 1) printf("%02X",(unsigned char)msg[i]);
    }

    if (DEBUG_OUT > 1) printf("Parse Data Frame:(Sockfd-%d, Devid-%d, Count-%d)\n",sockfd,devid,count);
#endif

    dataresp.command = msg[0];	// Command ID
    dataresp.length = msg[1];	// Data Length
    dataptr = &msg[2];			// Data buffer Start
    dataval_16 = *(unsigned short*)&msg[2];	// Data of 16 bits
    dataval_32 = *(unsigned int*)&msg[2];	// Data of 32 bits
    dataval_8 =  *(unsigned char*)&msg[2];	// Data of 8bits 

	// The reports don't carry a channel address so we use the global channel setting
	if ((lda[devid].HaveNumChannels) || (lda[devid].NumChannels != 0))
	{	
		if(lda[devid].Channel < lda[devid].NumChannels)
    		chnl = lda[devid].Channel;
		else
			chnl = lda[devid].NumChannels - 1;
	}
	else
	{
		chnl = 0;	// we don't know how many channels the device has yet
	}

    switch(dataresp.command)
	{

        case VNX_PWR:
            lda[devid].Attenuation[chnl] = dataval_16;
            break;

        case VNX_FREQUENCY:
            lda[devid].WorkingFrequency[chnl] = dataval_32;
            break;

        case VNX_SWEEP:
			// merge in the ramp mode bits from the device
			lda[devid].Modebits[chnl] = lda[devid].Modebits[chnl] & (~0xff);
            lda[devid].Modebits[chnl] |= dataval_8;
            break;

        case VNX_RFMUTE:
            if(dataval_16)
                lda[devid].Modebits[chnl] = lda[devid].Modebits[chnl] | MODE_RFON;
            else
                lda[devid].Modebits[chnl] = lda[devid].Modebits[chnl] & ~MODE_RFON;
            break;

        case VNX_ASTART:
            lda[devid].RampStart[chnl] = dataval_16;
            break;

        case VNX_ASTOP:
            lda[devid].RampStop[chnl] = dataval_16;
            break;

        case VNX_ASTEP:
            lda[devid].AttenuationStep[chnl] = dataval_16;
            break;

        case VNX_ASTEP2:
            lda[devid].AttenuationStep2[chnl] = dataval_16;
            break;

        case VNX_ADWELL:
            lda[devid].DwellTime[chnl] = dataval_32;
            break;

        case VNX_ADWELL2:
            lda[devid].DwellTime2[chnl] = dataval_32;
            break;

        case VNX_AIDLE:
            lda[devid].IdleTime[chnl] = dataval_32;
            break;

        case VNX_AHOLD:
            lda[devid].HoldTime[chnl] = dataval_32;
            break;

        case VNX_MINATTEN:
            lda[devid].MinAttenuation = dataval_16;
            break;

        case VNX_MAXATTEN:
            lda[devid].MaxAttenuation = dataval_16;
            break;

		case VNX_MAXCHANNEL:
			if (lda[devid].HaveNumChannels == 0)
			{
				if (dataval_32 <= CHANNEL_MAX)
				{
					// we have a reasonable number of channels for this device
					lda[devid].NumChannels = dataval_32;
					lda[devid].HaveNumChannels = TRUE;
				}
			}
			else
			{
				if (dataval_32 != lda[devid].NumChannels)
				{
					if (DEBUG_OUT > 0) printf("Default MaxChannel = %d, new MaxChannel = %d\n", lda[devid].NumChannels, dataval_32);
				}
			}
			
			// update the LDA-802-X name if necessary with the actual number of channels
			// it is the only LDA device with this behavior
			if ((lda[devid].DevType == LDA_802_8) && (dataval_32 != lda[devid].NumChannels))
			{
				sprintf(lda[devid].ModelName,"LDA-802-%d",lda[devid].NumChannels);
			}
			break;

        case VNX_GETSERNUM:
            lda[devid].SerialNumber = dataval_32;
            break;

			// The model name is used to identify the device type
			// The VNX_MODELNAME function returns the last 6 characters of the model name from
			// the device. We use that (or a part of it) to identify the device and set DevType
			// for non-expandable devices we set the maximum number of channels at this point
			//

        case VNX_MODELNAME:
			if (DEBUG_OUT > 1)
			{
				printf("In ModelName [packet]: ");
    			for(i = 0; i < count; i++)
    			{
        			printf("%02X ",(unsigned char)msg[i]);
    			}
				printf("\n");
			}

			// build a string from the 6 name bytes we received
			for(i = 0; i < 6; i++)
			{
				namebuffer[i] = msg[2+i];
			}
			namebuffer[6] = 0;		

			if(strstr(namebuffer,"-403"))
			{
				strcpy (lda[devid].ModelName, "LDA-403");	// save the device's model name
				if(!lda[devid].HaveNumChannels)
				{
					lda[devid].NumChannels = 1;	// this is an expandable single channel device
												// set a default
					lda[devid].BaseChannels = 1;
					lda[devid].DevType = LDA_403;
					lda[devid].MaxAttenuation = 630;	// 31.5db in .05db units
					lda[devid].MinFrequency = 10;		// 1 MHz
					lda[devid].MaxFrequency = 400000;	// 40 GHz		
				}			
			}
			if(strstr(namebuffer,"-203B"))
			{
			 	strcpy (lda[devid].ModelName, "LDA-203B");
				lda[devid].DevType = LDA_203;		// the socket interface uses standard commands
				lda[devid].NumChannels = 1;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 1260;	// 63db in .05db units
				lda[devid].MinFrequency = 10000;	// 1 GHz
				lda[devid].MaxFrequency = 200000;	// 20 GHz
			}
			if(strstr(namebuffer,"-802EH"))
			{
			 	strcpy (lda[devid].ModelName, "LDA-802EH");
				lda[devid].DevType = LDA_802EH;
				lda[devid].NumChannels = 1;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 2400;	// 120 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
			}
			else if (strstr(namebuffer,"608V-4"))
			{
				strcpy (lda[devid].ModelName, "LDA-608V-4");
				lda[devid].DevType = LDA_608V_4;
				lda[devid].NumChannels = 4;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 1200;	// 60 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
			}
			else if (strstr(namebuffer,"608V-2"))
			{
				strcpy (lda[devid].ModelName, "LDA-608V-2");
				lda[devid].DevType = LDA_608V_2;
				lda[devid].NumChannels = 2;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 1200;	// 60 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
			}
			else if (strstr(namebuffer,"A-608V"))
			{
				strcpy (lda[devid].ModelName, "LDA-608V");
				lda[devid].DevType = LDA_608V;
				lda[devid].NumChannels = 1;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 1200;	// 60 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
			}
			else if (strstr(namebuffer,"A-908V"))
			{
				strcpy (lda[devid].ModelName, "LDA-908V");	// this is the non-expandable 1 channel 908V
				lda[devid].DevType = LDA_908V;
				lda[devid].NumChannels = 1;
				lda[devid].HaveNumChannels = TRUE;	// no need to query for expansion channels
				lda[devid].MaxAttenuation = 1800;	// 90 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
			}
			else if (strstr(namebuffer,"-802Q"))
			{
				strcpy (lda[devid].ModelName, "LDA-802Q");
				lda[devid].DevType = LDA_802Q;
				lda[devid].MaxAttenuation = 2400;	// 120 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz
				lda[devid].BaseChannels = 4;
			 	if(!lda[devid].HaveNumChannels)
				{
					lda[devid].NumChannels = 4;	// set a default, will get updated
				}
			}
			else if (strstr(namebuffer,"802-"))
			{
				lda[devid].DevType = LDA_802_8;	// preset the common case
				lda[devid].MaxAttenuation = 2400;	// 120 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz

				if(lda[devid].HaveNumChannels)
				{
					// RD - the LDA-802-8 base module is the only LDA that includes
					//		expansion channel counts in the product name
					//		It is possible to have 20 channels, 24 channels, etc.
					sprintf(lda[devid].ModelName,"LDA-802-%d",lda[devid].NumChannels);
					lda[devid].BaseChannels = 8;
				}
				else if (strstr(namebuffer,"802-4"))
				{
					strcpy (lda[devid].ModelName, "LDA-802-4");
					lda[devid].NumChannels = 4;
					lda[devid].BaseChannels = 4;
					lda[devid].HaveNumChannels = TRUE;
				}
			 	else if (strstr(namebuffer,"802-8"))
				{
					strcpy (lda[devid].ModelName, "LDA-802-8");
					lda[devid].NumChannels = 8;
					lda[devid].BaseChannels = 8;
					lda[devid].HaveNumChannels = TRUE;
				}
				else if (strstr(namebuffer,"802-12"))
				{
					// RD - the LDA-802-8 base module is the only LDA that includes
					//		expansion channel counts in the product name, so we can use them
					strcpy (lda[devid].ModelName, "LDA-802-12");
					lda[devid].NumChannels = 12;
					lda[devid].BaseChannels = 8;
					lda[devid].HaveNumChannels = TRUE;
				}
			 	else if (strstr(namebuffer,"802-16"))
				{
					strcpy (lda[devid].ModelName, "LDA-802-16");
					lda[devid].NumChannels = 16;
					lda[devid].BaseChannels = 8;
					lda[devid].HaveNumChannels = TRUE;
				}
			 	else if (strstr(namebuffer,"802-32"))
				{
					strcpy (lda[devid].ModelName, "LDA-802-32");
					lda[devid].NumChannels = 32;
					lda[devid].BaseChannels = 8;
					lda[devid].HaveNumChannels = TRUE;
				}
				else
				{
					// we can end up here if the HW has another number of channels
					// and the max channel query has not run yet
					strcpy (lda[devid].ModelName, "LDA-802-N");
					lda[devid].BaseChannels = 8;
				}				
			}
			else if (strstr(namebuffer,"X1-752"))
			{
				// RD possible future HW product, not available yet
				strcpy (lda[devid].ModelName, "LDA-8X1-752");
				lda[devid].DevType = LDA_8X1_752;
				lda[devid].MaxAttenuation = 1800;	// 90 db in .05db units
				lda[devid].MinFrequency = 5000;		// 500 MHz
				lda[devid].MaxFrequency = 60000;	// 6 GHz
				lda[devid].BaseChannels = 8;
			 	if(!lda[devid].HaveNumChannels)
				{
					lda[devid].NumChannels = 8;	// set a default, will get updated
				}

			}
			else if (strstr(namebuffer,"908V-"))
			{
				// The LDA-908V-N devices have an expansion bus, but they do not update
				// their name to reflect the total number of channels
				lda[devid].MaxAttenuation = 1800;	// 90 db in .05db units
				lda[devid].MinFrequency = 2000;		// 200 MHz
				lda[devid].MaxFrequency = 80000;	// 8 GHz

				if (strstr(namebuffer,"908V-4"))
				{
					strcpy (lda[devid].ModelName, "LDA-908V-4");
					lda[devid].DevType = LDA_908V_4;
					lda[devid].BaseChannels = 4;
					if(!lda[devid].HaveNumChannels)
					{
						lda[devid].NumChannels = 4; // set a default, will get updated
					}
				}
			 	else if (strstr(namebuffer,"908V-8"))
				{
					strcpy (lda[devid].ModelName, "LDA-908V-8");
					lda[devid].DevType = LDA_908V_8;
					lda[devid].BaseChannels = 8;
					if(!lda[devid].HaveNumChannels)
					{
						lda[devid].NumChannels = 8; // set a default, will get updated
					}
				}
				else if (strstr(namebuffer,"908V-2"))
				{
					strcpy (lda[devid].ModelName, "LDA-908V-2");
					lda[devid].DevType = LDA_908V_2;
					lda[devid].BaseChannels = 2;
					if(!lda[devid].HaveNumChannels)
					{
						lda[devid].NumChannels = 2; // set a default, will get updated
					}
				}
				else
				{
					// we should not end up here, if so we got a name we didn't expect
					strcpy (lda[devid].ModelName, "LDA-908V-N");
					lda[devid].BaseChannels = 8;
					lda[devid].DevType = 0;
				}
			}
			else
			{
				// RD!! - we should flag this error, for now its just a super-device
				strncpy (lda[devid].ModelName, namebuffer, 6);  // save the device's model name
				lda[devid].DevType = 0;
				lda[devid].NumChannels = 32;
				lda[devid].BaseChannels = 8;
				lda[devid].MaxAttenuation = 2400;	// 120 db in .05db units
				lda[devid].MinFrequency = 1000;		// 100 MHz
				lda[devid].MaxFrequency = 400000;	// 40 GHz
			}

			// set a few parameters for the device
			lda[devid].MinAttenuation = 0;
            lda[devid].ProfileMaxLength = 1000;

			if (DEBUG_OUT > 2) printf("Exiting ModelName...\n");
            break;

        case VNX_SWVERSION:
			// build a string from the 6 bytes we received
			for(i = 0; i < 6; i++)
			{
				lda[devid].Swversion[i] = msg[2+i];		// safe copy...
			}
			lda[devid].Swversion[6] = 0;				// the null terminator for the string	
            break;
            
        case VNX_IPMODE:
            lda[devid].ipmode = (int)dataval_8;
            break;

        case VNX_IPADDR:
            sprintf(lda[devid].ipaddress, "%d.%d.%d.%d", (dataval_32 >> 24) & 0xff, (dataval_32 >> 16) & 0xff,
                 (dataval_32 >> 8) & 0xff, dataval_32 & 0xff);
            break;

        case VNX_IPMASK:
            sprintf(lda[devid].netmask, "%d.%d.%d.%d", (dataval_32 >> 24) & 0xff, (dataval_32 >> 16) & 0xff,
                 (dataval_32 >> 8) & 0xff, dataval_32 & 0xff);
            break;

        case VNX_IPGW:
            sprintf(lda[devid].gateway, "%d.%d.%d.%d", (dataval_32 >> 24) & 0xff, (dataval_32 >> 16) & 0xff,
                 (dataval_32 >> 8) & 0xff, dataval_32 & 0xff);
            break;

        case VNX_MINFREQUENCY:
            lda[devid].MinFrequency = dataval_32;
            break;

        case VNX_MAXFREQUENCY:
            lda[devid].MaxFrequency = dataval_32;
            break;

        case VNX_CHANNEL:
            lda[devid].Channel = (int)dataval_8 - 1;	// The global channel number, 1 to NumChannels
														// internally we use zero based channel numbers
			if ((lda[devid].Channel >= lda[devid].NumChannels) || (lda[devid].Channel < 0))
			{
				lda[devid].Channel = lda[devid].NumChannels - 1;	// clip to protect our arrays...
			}
            break;

        case VNX_PROFILECOUNT:
            lda[devid].ProfileCount[chnl] = dataval_16;
            break;

        case VNX_PROFILEDWELL:
            lda[devid].ProfileDwellTime[chnl] = dataval_32;
            break;

        case VNX_PROFILEIDLE:
            lda[devid].ProfileIdleTime[chnl] = dataval_32;
            break;

        case VNX_SETPROFILE:
			prfindex = *(unsigned short*)&msg[6];   // Data of 16 bits
			if (prfindex < PROFILE_MAX_RAM) 		// defend our array
			{
            	lda[devid].Profile[chnl][prfindex] = dataval_16;
			}
            break;

        default:
            break;
    }

    lda[devid].responseready = 1;  // Device Response Received

    return STATUS_OK;
}

/*******************************************************************************
 * \brief    Reads Data from the socket
 ******************************************************************************/
static int readdatafromSock(int sockfd, int devid)
{
    int count;
    char *buf_pr;
    char buf_st[512] = {0};
    int index;

    buf_pr = buf_st;
        
    if ((count = read(sockfd, buf_pr, 512)) >= 0) {

        if (DEBUG_OUT > 1) printf("READ FROM SOCKET(%d): Count-%d\n", sockfd, count);

        // Check Socket data response > 8 then split into 8 bytes chunks and parse the command data
        if(count > HID_REPORT_LENGTH)
        {
            for(index=0; index < count; index+=8)
            {
                if (DEBUG_OUT > 1) printf("Send Frame Data(%d,%d)\n",index, count);
                parseDataFrame(buf_pr+index, 8, sockfd, devid);
            }
        }
        else
            parseDataFrame(buf_pr, count, sockfd, devid);
    }
    return 0;
}

/*******************************************************************************
 * \brief    Callback for Receiver data handler of thread
 * NB -- the threadID is just an integer ID, not a pointer, it is just smuggled in
 *       as if it was a pointer
 ******************************************************************************/
static void *rxdatahandler_callback (void *threadID) {
  int tid, ret = 0;;
  struct pollfd poll_fd[1];
  tid = (int)(uintptr_t)threadID;

  if (DEBUG_OUT > 1) printf("Thread Callback:%d\n",tid);

  for (;;) {
      // poll for reading responses
      poll_fd[0].fd = lda[tid].devicesockfd;
      poll_fd[0].events = POLLIN | POLLRDNORM;
      poll_fd[0].revents = 0;
            
      for(;;)
      {
            ret = poll(poll_fd, 1, -1);
            if (ret > 0)
            {
                if (poll_fd[0].revents & POLLIN || poll_fd[0].revents & POLLRDNORM)
                {
                    poll_fd[0].revents = 0;
                    // Read data
                    readdatafromSock(lda[tid].devicesockfd, tid);
                }
            }    
        }
    }
    return 0;
}

//*****************************************************************************
//
// Function call to Set channel# to the device
// Channel is 1 to N at the API level, 0 to N-1 internally
//
// RD 7/12/24 changed to use a more backwards compatible addressing command format
//            with a goal of implementing full addressing in commands in the future
//  
//*****************************************************************************
static int SetChannel(char* deviceip, int channel)
{
    int devid = 0;
	char VNX_command[] = { 0, 0, 0, 0, 0, 0 };

    devid = GetDeviceID(deviceip);
	if (devid < 0) return STATUS_ERROR;

    if (!isDeviceSockOpen(deviceip)) return STATUS_ERROR;
	
	if ((channel < 1) || (channel > lda[devid].NumChannels)) return STATUS_ERROR;

	// internally we use a zero based channel number
	lda[devid].Channel = channel - 1;

	if (TestMode)
	{
		// RD - no need to emulate the mask bits...
		return STATUS_OK;
	}
        
    // convert to our mask form, and save that and our local zero based channel number
	// the mask based address consists of two bank bits and an 8 bit wide mask. Each logical bank
	// has 8 channels.
	// The global channel number, used for channels in the expansion devices, runs 1 to N
	lda[devid].ChMask = 0x01 << ((channel - 1) % 8);	// logical channel selection mask
	lda[devid].ChBank = (channel - 1) / 8;				// internally ChBank is zero based

	// no need to send set channel commands to a single channel device
	if (lda[devid].NumChannels == 1) return STATUS_OK;

	// VNX_command[5] = (char)lda[devid].ChMask;
	// VNX_command[4] = (char)(lda[devid].ChBank + 1);		// RD - this is the full bank/mask addressing
	// RD 7-12-24 not all HW supports the full bank/mask addressing, use one of the legacy
	//            channel address models
	//

	if (channel <= lda[devid].BaseChannels)
	{
			VNX_command[5] = (char)lda[devid].ChMask;
	}
	else
		VNX_command[0] = (char)channel;
           
	if (!SendReport(devid, VNX_CHANNEL | VNX_SET, VNX_command, 6))
		return STATUS_ERROR;

    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get the parameter from the device using message
//  
//*****************************************************************************
static bool GetParameter(char* deviceip, int GetParam)
{
    char VNX_param[6] = {0, 0, 0, 0, 0, 0};
    int devid=0;

    devid = GetDeviceID(deviceip);

    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		// for test mode we don't have to do anything
		if (TestMode) return STATUS_OK;

        if (!SendReport(devid, GetParam, VNX_param, 0)) {
          return STATUS_ERROR;
        }       
    }
    else 
        return STATUS_ERROR;
    return  STATUS_OK;
}

//***************************************************************************
//
// Function call to Get the parameter from the device using message
//  
//*****************************************************************************
static bool GetParameterWithIndex(char* deviceip, int GetParam, int index)
{
    char VNX_param[6] = {0, 0, 0, 0, 0, 0};
    int devid=0;

    devid = GetDeviceID(deviceip);

    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		// for test mode we don't have to do anything
		if (TestMode) return STATUS_OK;

			VNX_param[3] = ((index >> 8) & 0x03);
			VNX_param[4] = index;
        if (!SendReport(devid, GetParam, VNX_param, 5)) {
          return STATUS_ERROR;
        }       
    }
    else 
        return STATUS_ERROR;
    return  STATUS_OK;
}


//***************************************************************************
//
// Function call to Initialize all the library data structures with default data
//  
//*****************************************************************************
void fnLDA_Init(void) {
    /* clear out the storage structure. Must be called once before anything else */
    int index,index2;
     
    for (index = 0; index<MAXDEVICES+1; index++){
        ClearDevCache(index, CHANNEL_MAX);  // clear all cached variables to the unknown state

        lda[index].SerialNumber = 0;        // clear the serial number
        lda[index].ModelName[0] = 0;        // put a null string in each model name field
        lda[index].devicesockfd = 0;        // clear the device handles
		lda[index].HaveNumChannels = FALSE;	// we don't know how many channels the device has
        lda[index].deviceready = 0;			// its definitely not ready
        lda[index].responseready = 0;       // Device Response ready status  
        strcpy(lda[index].deviceip, "");
    }

    if (DEBUG_OUT > 0)  printf("Linux SDK version %s\r\n", fnLDA_LibVersion());
}

//***************************************************************************
//
// Function call to Initialize the LDA Device and Open the socket connection
// to that device for communication
//
//	RD 5/24 lots of changes. Active handles now start at 1, so the array is now
//			one element larger. The test mode device lives at array entry zero.
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_InitDevice(char* deviceip) {
    int sockfd;
    int devid, err;
    unsigned int i;

    // Check whether we reached max device count
    if ((ldadevcount >= MAXDEVICES)) {
		// no room in our table, not much we can do        
		return STATUS_ERROR;
    }

    //check device ip format 
    if(!is_valid_ip(deviceip))
        return STATUS_ERROR;

	// see if that IP address is already in our table of devices
	if (GetDeviceID(deviceip) > 0)
	{
		// this is an IP address we already have open, so we will fail this attempt
		// allowing multiple programs to interact with our socket is likely to cause chaos
		return STATUS_ERROR;
	}

	// this is a new IP address, we need to find a slot to park it in,
	// our handles start at 1
	for (i = 1; i < MAXDEVICES+1; i++)
	{
		if (lda[i].deviceip[0] == 0)		// a quick test for a null string
		{
			devid = i;
			break;
		}
	} 

   	// Copy the device IP to the data structure
   	strcpy(lda[devid].deviceip, deviceip);

   	if (TestMode)
	{    
       	lda[devid].devicesockfd = 1234;
    	lda[devid].deviceready = 1;

		// create a fake LDA-802-8
		strcpy (lda[devid].ModelName, "LDA-802-8");
		lda[devid].DevType = LDA_802_8;
		lda[devid].SerialNumber = 12001 + ldadevcount;
		lda[devid].MaxAttenuation = 2400;		// 120 db in .05db units
		lda[devid].MinFrequency = 2000;			// 200 MHz
		lda[devid].MaxFrequency = 80000;		// 8 GHz
		lda[devid].NumChannels = 8;
		lda[devid].HaveNumChannels = TRUE;
		lda[devid].MinAttenuation = 0;
		lda[devid].ProfileMaxLength = 1000;
		lda[devid].Channel = 0;					// we start on channel 1
		lda[devid].Attenuation[0] = 0;			// 0 db attenuation
		lda[devid].WorkingFrequency[0] = 10000;	// 1 GHz working frequency
		lda[devid].RampStart[0] = 500;			// 25 db ramp start
		lda[devid].RampStop[0] = 1000;			// 50 db ramp end
		lda[devid].AttenuationStep[0] = 20;     // 1 db ramp step size for the first phase of the ramp
		lda[devid].AttenuationStep2[0] = 40;    // 2 db ramp step size for second phase of the ramp
		lda[devid].DwellTime[0] = 1000;			// 1 sec per step for the first phase of the ramp
		lda[devid].DwellTime2[0] = 500;			// 500 msec per step for the second phase of the ramp
		lda[devid].IdleTime[0] = 100;			// 100 msec idle time between ramp repeats
		lda[devid].HoldTime[0] = 200;			// 200 msec hold time between phases
		lda[devid].ProfileCount[0] = 2;			// 2 elements in the profile
		lda[devid].ProfileDwellTime[0] = 1200;	// 1.2 sec profile dwell time
		lda[devid].ProfileIdleTime[0] = 0;		// no idle time for repeating profiles
		lda[devid].Profile[0][0] = 600;			// 30 db first profile element
		lda[devid].Profile[0][1] = 400;			// 20 db first profile element

       	ldadevcount++;
   		return STATUS_OK;
   	}

	// Go ahead and open a handle to the hardware
	sockfd = vnxDeviceOpen(deviceip);
	if (sockfd == -1)  // vnxDeviceOpen returns 0 (STATUS_OK) if the open succeeded, otherwise an error code
	{         
		// our open failed, so clean up the slot we tried to use
		strcpy(lda[devid].deviceip,"");
		lda[devid].devicesockfd = 0;		// RD -- redundant, but fast...
		return STATUS_ERROR;
	}

	lda[devid].devicesockfd = sockfd;
	ldadevcount++;

	if (DEBUG_OUT > 0) printf("Device Opened: Devid: %d, Socketfd: %d, DevCount: %d\n",devid, sockfd, ldadevcount);
 
	// Create a device threads for reading the data from the device using sockets
	err = pthread_create(&threads[devid], NULL, rxdatahandler_callback, (void*)(uintptr_t)devid);


	// RD!! - nobody reads the cached values, and lots of code opens and closes the device
	//		  at high rates. This code just generated traffic and slows performance for no purpose
	//		  The replacement gets the parameters we need to have - device type, number of channels, 
	//		  and current channel.

#if 0
    // Get the current status details of the device parameter
    for(index=0; index < sizeof(STATUS_QUERY_ID); index++)
    {
        GetParameter(deviceip, STATUS_QUERY_ID[index]);
    }
#endif

	// determine what device we have. This process also sets the DevType and NumChannels
	// for devices where the number of channels is not expandable.
	GetParameter(deviceip, VNX_MODELNAME);

	// for expandable devices determine how many channels are present
	if (!lda[devid].HaveNumChannels)
	{
		GetParameter(deviceip, VNX_MAXCHANNEL);
	}

	// find out what the device's active channel is
	if (lda[devid].NumChannels > 1)
	{
		GetParameter(deviceip, VNX_CHANNEL);	// once we have the number of channels and the active channel
												// we are ready to roll
	}

    // if we got here everything worked OK
    lda[devid].deviceready = 1;
    return STATUS_OK;
}


//***************************************************************************
//
// Function call to close the socket of the device open
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_CloseDevice(char* deviceip) {
    int sockfd, devid;
	int result;
        
    //check device ip format 
    if(!is_valid_ip(deviceip))
      return STATUS_ERROR;

    // Check Device open
    if(isDeviceSockOpen(deviceip))
    {
        devid = GetDeviceID(deviceip);
        // If Device exists then close it
        if(devid >= 0)
        {
			// clear the stored data from this device
            strcpy(lda[devid].deviceip,"");
			lda[devid].HaveNumChannels = 0;

			lda[devid].DevType = 0;
			lda[devid].deviceready = 0;
			lda[devid].responseready = 0;

			ClearDevCache(devid, lda[devid].NumChannels);  // clear the cached variables to the unknown state
			lda[devid].NumChannels = 0;

            ldadevcount--;
            if (!TestMode){
                sockfd = lda[devid].devicesockfd;
                result = close(sockfd);
				if (DEBUG_OUT > 0) printf("After close Result: %d, Socketfd:%d\n", result, sockfd);
            }
            lda[devid].devicesockfd = 0;
        }
        else
            return STATUS_ERROR;
    }
    else
        return STATUS_ERROR;

  return STATUS_OK;

}


//***************************************************************************
//
// Function call to Check if the LDA Device is ready or not
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_CheckDeviceReady(char* deviceip ) {
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    if (DEBUG_OUT > 1) printf("in checkdeviceready devid =  %d, deviceready = %d\n", devid, lda[devid].deviceready);

    // check if this device is ready 
    if((devid >= 0) && (lda[devid].deviceready != 0))
    {
        return STATUS_OK;
    }
    else
        return STATUS_ERROR;
}

//***************************************************************************
//
// Function call to Get Maxchannels from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetMaxChannels(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MAXCHANNEL);
        *respdata = lda[devid].NumChannels;
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}


//***************************************************************************
//
// Function call to Get Model name from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetModelName(char* deviceip, char *ModelName) {
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MODELNAME);
        strcpy(ModelName, lda[devid].ModelName);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Serial# from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetSerialNumber(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_GETSERNUM);
        *respdata = lda[devid].SerialNumber;
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Software Version from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetSoftwareVersion(char* deviceip, char* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_SWVERSION);
        strcpy(respdata, lda[devid].Swversion);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get IP Mode from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetIPMode(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_IPMODE);
        *respdata = lda[devid].ipmode;
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get IP Address from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetIPAddress(char* deviceip, char* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_IPADDR);
        strcpy(respdata, lda[devid].ipaddress);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Netmask from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetNetmask(char* deviceip, char* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_IPMASK);
        strcpy(respdata, lda[devid].netmask);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Gateway from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetGateway(char* deviceip, char* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_IPGW);
        strcpy(respdata, lda[devid].gateway);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get current Frequency from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetWorkingFrequency(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_FREQUENCY);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata = lda[devid].WorkingFrequency[ch];
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Min Frequency from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetMinWorkingFrequency(char* deviceip, int* respdata){
    int devid=0;
   if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MINFREQUENCY);
        *respdata = lda[devid].MinFrequency; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Max Frequency from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetMaxWorkingFrequency(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MAXFREQUENCY);
        *respdata = lda[devid].MaxFrequency; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Min Attenuation from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetMinAttenuation(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MINATTEN);
        *respdata = lda[devid].MinAttenuation; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}


//***************************************************************************
//
// Function call to Get Max Attenuation from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetMaxAttenuation(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_MAXATTEN);
        *respdata = lda[devid].MaxAttenuation; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get current Channel from the device
//
// The internal channel number is zero based, the API channel number is 1 based
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetChannel(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data and adjust to the API format 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_CHANNEL);
        *respdata = (lda[devid].Channel + 1);
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}


//***************************************************************************
//
// Function call to Get current Attenuation from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetAttenuation(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_PWR);
        
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].Attenuation[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Ramp Start Attenuation from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetRampStart(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_ASTART);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].RampStart[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Ramp End Attenuation from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetRampEnd(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_ASTOP);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].RampStop[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Dwell Time from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetDwellTime(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_ADWELL);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].DwellTime[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Dwell Time two from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetDwellTimeTwo(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_ADWELL2);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].DwellTime2[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Idle Time from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetIdleTime(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_AIDLE);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].IdleTime[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Hold Time from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetHoldTime(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If the device is available read the hold time 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_AHOLD);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].HoldTime[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Attenuation Step Size from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetAttenuationStep(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_AHOLD);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].AttenuationStep[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Attenuation Step Two Size from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetAttenuationStepTwo(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_AHOLD);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].AttenuationStep2[ch]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Profile Maxlength from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetProfileMaxLength(char* deviceip, int* respdata){
    int devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        *respdata =lda[devid].ProfileMaxLength; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Profile Element from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetProfileElement(char* deviceip, int index, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameterWithIndex(deviceip, VNX_SETPROFILE, index);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].Profile[ch][index]; 
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Profile Count from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetProfileCount(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_PROFILECOUNT);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata = lda[devid].ProfileCount[ch];
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Profile Dwell Time from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetProfileDwellTime(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_PROFILEDWELL);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata =lda[devid].ProfileDwellTime[ch];
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call to Get Profile Idle Time from the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_GetProfileIdleTime(char* deviceip, int* respdata){
    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
        GetParameter(deviceip, VNX_PROFILEIDLE);
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        *respdata = lda[devid].ProfileIdleTime[ch];
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//***************************************************************************
//
// Function call for Get a set of bits describing the features available in this attenuator device
//  
//***************************************************************************
STATUS_REPORT_T fnLDA_GetFeatures(char* deviceip, int* respdata) {
	int licdata = DEFAULT_FEATURES;

    int ch, devid=0;
    if (!isDeviceSockOpen(deviceip))
        return STATUS_ERROR;

    devid = GetDeviceID(deviceip);

    // If Device exists then read the data 
    if(devid >= 0)
    {
		licdata = (HAS_HIRES | HAS_BIDIR_RAMPS | HAS_PROFILES);// HiRes has bidirectional ramps, profiles and HiRes
		if (lda[devid].NumChannels == 4)
			licdata |= HAS_4CHANNELS;

		if (lda[devid].NumChannels == 8)   
			licdata |= HAS_8CHANNELS;
		
		licdata |= HAS_LONG_PROFILE;

		*respdata = licdata;
    }
    else {
        return STATUS_ERROR;
    }
	return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set current operating frequency to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetWorkingFrequency(char* deviceip, int frequency) {
    int devid=0;
    int old_frequency,tmp_frequency = 0;
    int ch = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);

    if( devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_frequency = lda[devid].WorkingFrequency[ch];

        if (DEBUG_OUT > 0) {
          printf("Min frequency is %d\r\n", lda[devid].MinFrequency);
          printf("Max frequency is %d\r\n", lda[devid].MaxFrequency);
          printf("User frequency to set is %d\r\n", frequency);
        }

        if ((frequency >= lda[devid].MinFrequency) && (frequency <= lda[devid].MaxFrequency)) {
          lda[devid].WorkingFrequency[ch] = frequency;
          if (TestMode)
            return STATUS_OK;       // in test mode we update our internal variables, but don't talk to the real HW
        } else {
          return STATUS_ERROR;
        }

        // the working frequency value is OK, lets send it to the hardware
        tmp_frequency = frequency;
        ptr = (char *) &tmp_frequency;

        if (DEBUG_OUT > 0) printf("setting frequency to %d\r\n", tmp_frequency);

        if (!SendReport(devid, VNX_FREQUENCY | VNX_SET, ptr, 4)) {
          lda[devid].WorkingFrequency[ch] = old_frequency;
          return STATUS_ERROR;
        }       
        return STATUS_OK;
    }
    else
        return STATUS_ERROR;
}

//*****************************************************************************
//
// Function call to Set channel# to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetChannel(char* deviceip, int channel)
{
    return SetChannel(deviceip, channel);
}

//*****************************************************************************
//
// Function call to Set Attenuation data to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetAttenuation(char* deviceip, int attenuation) {

    int tmp_attenuation = 0;
    int old_attenuation, devid=0;
	int ch;
    char *ptr;

    devid = GetDeviceID(deviceip);

    if( devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        old_attenuation = lda[devid].Attenuation[ch];
        if ((attenuation >= lda[devid].MinAttenuation) && (attenuation <= lda[devid].MaxAttenuation))
		{ 
            lda[devid].Attenuation[ch] = attenuation;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
        else
			return STATUS_ERROR;

        tmp_attenuation = attenuation;
        ptr = (char *) &tmp_attenuation;

        if (DEBUG_OUT > 0) printf("setting attenuation to %d\r\n", tmp_attenuation);
        
        if (!SendReport(devid, VNX_PWR | VNX_SET, ptr, 4)) {
          lda[devid].Attenuation[ch] = old_attenuation;
          return STATUS_ERROR;
        }       
    }
    else 
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call set attenuation on multiple channels of the device 
// chmask has a 1 bit set for every channel to be set
//
// RD 5/2024 experimental code
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetAttenuationMC(char* deviceip, int attenuation, int chmask)
{
    int bank, tmp;
    unsigned int ch, i;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

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

		// limit the active channels based on the number of channels in the device
		tmp = 0xFFFFFFFF >> (32 - lda[devid].NumChannels);
		chmask &= tmp;	// clip off any extraneous select bits

		// make sure the attenuation value is in range for this device
        if ((attenuation >= lda[devid].MinAttenuation) && (attenuation <= lda[devid].MaxAttenuation))
		{
				// save the new attenuation values in our local cache
 				tmp = 1;
				for (i = 0; i < lda[devid].NumChannels; i++)
				{
					if (tmp & chmask)
					{
						lda[devid].Attenuation[i] = attenuation;
					}
				
					tmp = tmp << 1;
				}

 				if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
        else
			return STATUS_ERROR;

		// put the attenuation value into our command
		VNX_command[0] = (BYTE)(attenuation & 0x000000ff);  // low byte of attenuation in .05 db device units
        VNX_command[1] = (BYTE)((attenuation & 0x0000ff00) >> 8);   // high byte of attenuation

		// we are ready to send the multiple channel attenuation commands to the hardware
		for (i = 0; i < 4; i++)
		{
			tmp = chmask >> ((8 * i) & 0xff);
			if (tmp != 0)
			{
				// we have channels selected in this bank
				VNX_command[4] = i + 1; 
        		VNX_command[5] = (BYTE)tmp;	// the channel bit mask for this bank

        		if (!SendReport(devid , VNX_PWR | VNX_SET, (char *)VNX_command, 6))
				{
            		return STATUS_ERROR;
        		}
			}
		}
    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Attenuation data of channel# to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetAttenuationQ(char* deviceip, int attenuation, int channel) {

    int tmp_attenuation = 0;
    STATUS_REPORT_T SCResult;
    int devid=0;

    devid = GetDeviceID(deviceip);
    if( devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

        // set the channel first
        SCResult = (STATUS_REPORT_T) SetChannel(deviceip, channel);
        if ( SCResult != STATUS_OK) return SCResult;

        fnLDA_SetAttenuation(deviceip, attenuation);
    }
    else 
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Attenuation Ramp Step Size(First Phase) to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetAttenuationStep(char* deviceip, int attenuationstep) {
    int old_attenuationstep, tmp_attenuationstep = 0;
    int ch, devid=0;
    char *ptr;

    devid = GetDeviceID(deviceip);

    if(devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_attenuationstep = lda[devid].AttenuationStep[ch];

        if (attenuationstep < (lda[devid].MaxAttenuation - lda[devid].MinAttenuation))
		{
            lda[devid].AttenuationStep[ch] = attenuationstep;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;
      
        tmp_attenuationstep = attenuationstep;
        ptr = (char *) &tmp_attenuationstep;

        if (!SendReport(devid, VNX_ASTEP | VNX_SET, ptr, 4)) {
            lda[devid].AttenuationStep[ch] = old_attenuationstep;
            return STATUS_ERROR;
        }
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Attenuation Ramp Step Size(Second Phase) to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetAttenuationStepTwo(char* deviceip, int attenuationstep2) {
    int old_attenuationstep, tmp_attenuationstep = 0;
    int ch, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);

    if(devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_attenuationstep = lda[devid].AttenuationStep2[ch];

        if (attenuationstep2 < (lda[devid].MaxAttenuation - lda[devid].MinAttenuation))
		{ 
            lda[devid].AttenuationStep2[ch] = attenuationstep2;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;
      
        tmp_attenuationstep = attenuationstep2;
        ptr = (char *) &tmp_attenuationstep;

        if (!SendReport(devid, VNX_ASTEP2 | VNX_SET, ptr, 4)) {
            lda[devid].AttenuationStep2[ch] = old_attenuationstep;
            return STATUS_ERROR;
        }
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Ramp Start Attenuation data to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRampStart(char* deviceip, int rampstart) {
    int tmp_rampstart = 0;
    int ch, old_rampstart, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);

    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
        old_rampstart = lda[devid].RampStart[ch];

        if ((rampstart >= lda[devid].MinAttenuation) && (rampstart <= lda[devid].MaxAttenuation))
		{
            lda[devid].RampStart[ch] = rampstart;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;

        tmp_rampstart = rampstart;
        ptr = (char *) &tmp_rampstart;

        if (!SendReport(devid, VNX_ASTART | VNX_SET, ptr, 4)){
            lda[devid].RampStart[ch] = old_rampstart;
            return STATUS_ERROR;
        }
    }
    else 
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Ramp End Attenuation data to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRampEnd(char* deviceip, int rampstop) {
    int tmp_rampstop = 0;
    int ch, old_rampstop, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_rampstop = lda[devid].RampStop[ch];

        if ((rampstop >= lda[devid].MinAttenuation) && (rampstop <= lda[devid].MaxAttenuation))
		{
            lda[devid].RampStop[ch] = rampstop;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;

        tmp_rampstop = rampstop;
        ptr = (char *) &tmp_rampstop;

        if (!SendReport(devid, VNX_ASTOP | VNX_SET, ptr, 4)) {
            lda[devid].RampStop[ch] = old_rampstop;
            return STATUS_ERROR;
        }
    }
    else 
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Dwell Time data(First Phase Ramp) to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetDwellTime(char* deviceip, int dwelltime) {
    int ch = 0;
    int old_dwelltime,devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_dwelltime = lda[devid].DwellTime[ch];

        if (dwelltime >= VNX_MIN_DWELLTIME)
		{
            lda[devid].DwellTime[ch] = dwelltime;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;

        // the dwell time value is OK, lets send it to the hardware
        ptr = (char *) &lda[devid].DwellTime[ch];

        if (!SendReport(devid, VNX_ADWELL | VNX_SET, ptr, 4)) {
            lda[devid].DwellTime[ch] = old_dwelltime;
            return STATUS_ERROR;
        }
    }
    else 
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Dwell Time Two data(Second Phase Ramp) to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetDwellTimeTwo(char* deviceip, int dwelltime2) {
    int ch = 0;
    int old_dwelltime2, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_dwelltime2 = lda[devid].DwellTime2[ch];

        if (dwelltime2 >= VNX_MIN_DWELLTIME)
		{ 
            lda[devid].DwellTime2[ch] = dwelltime2;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;
      
        // the dwell time value is OK, lets send it to the hardware
        ptr = (char *) &lda[devid].DwellTime2[ch];

        if (!SendReport(devid, VNX_ADWELL2 | VNX_SET, ptr, 4)) {
            lda[devid].DwellTime2[ch] = old_dwelltime2;
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Idle Time data(Wait time @end of Ramp) to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetIdleTime(char* deviceip, int idletime) {
    int ch = 0;
    int old_idletime, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_idletime = lda[devid].IdleTime[ch];

        if (idletime >= 0)		// idle time can be zero
		{ 
            lda[devid].IdleTime[ch] = idletime;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;
     
        ptr = (char *) &lda[devid].IdleTime[ch];

        if (!SendReport(devid, VNX_AIDLE | VNX_SET, ptr, 4)) {
            lda[devid].IdleTime[ch] = old_idletime;
            return STATUS_ERROR;
        }
    }
    else 
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Hold Time data(Wait time btw First and Second Phase)
// to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetHoldTime(char* deviceip, int holdtime) {
    int ch = 0;
    int old_holdtime, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_holdtime = lda[devid].HoldTime[ch];

        if (holdtime >= 0)	// holdtime can be zero
		{
            lda[devid].HoldTime[ch] = holdtime;
          	if (TestMode) return STATUS_OK;  // for test mode we just update internal variables
		}
		else
			return STATUS_ERROR;
     
        ptr = (char *) &lda[devid].HoldTime[ch];

        if (!SendReport(devid, VNX_AHOLD | VNX_SET, ptr, 4)) {
            lda[devid].HoldTime[ch] = old_holdtime;
            return STATUS_ERROR;
        }
    }
    else 
        return STATUS_ERROR;
  
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set RF On/Off state to the device (not supported by all attenuators)
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRFOn(char* deviceip, bool on) {
    int ch = 0;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;
    
        char VNX_command[] = {0, 0, 0, 0};

        if (on) {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~MODE_RFON;
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] | MODE_RFON;
            VNX_command[0] = 1;
        } else  {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~MODE_RFON;
            VNX_command[0] = 0;
        }

        if (TestMode) return STATUS_OK;  // for test mode we just update internal variables

        if (!SendReport(devid, VNX_RFMUTE | VNX_SET, VNX_command, 1))
          return STATUS_ERROR;
    }
    else
        STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Ramp Direction to the device , Up = True (Upwards)
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRampDirection(char* deviceip, bool up) {
    int ch = 0;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        if (up)
        {
			// ramp direction up (bit == 0)
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~SWP_DIRECTION;
        }
        else
        {
 			// ramp direction downwards
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] | SWP_DIRECTION;
        }
    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Ramp Mode to the device , Mode = True (Repeated Ramp)
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRampMode(char* deviceip, bool mode) {
    int ch = 0;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check if this device is open
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        if (mode) {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] | SWP_CONTINUOUS;     // Repeated ramp or sweep
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~SWP_ONCE;
        } else {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] | SWP_ONCE;           // one time ramp or sweep
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~SWP_CONTINUOUS;
        }
    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Birectional Mode to the device , bidir_enable = True (Bidirectional)
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetRampBidirectional(char* deviceip, bool bidir_enable) {
    int ch = 0;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check if device is open
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        if (bidir_enable)
        {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] | SWP_BIDIR;    // bidirectional ramp
        }
        else
        {
            lda[devid].Modebits[ch] = lda[devid].Modebits[ch] & ~SWP_BIDIR;   // unidirectional ramp
        }
    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Profile Element data to the device
//  
//*****************************************************************************
 STATUS_REPORT_T fnLDA_SetProfileElement(char* deviceip, int index, int attenuation) {
    int ch = 0;
    int devid=0;
    int max_index, old_element;
    BYTE VNX_command[] = {0, 0, 0, 0, 0, 0, 0};
    
    devid = GetDeviceID(deviceip);
    if( devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;
        
		if(lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        max_index = lda[devid].ProfileMaxLength - 1;

		if (max_index < 0 || max_index > 999) max_index = 999;		// defend our arrays

		if (index < 0 || index > max_index) return STATUS_ERROR;	// invalid profile index
        
        if ((attenuation >= lda[devid].MinAttenuation) && (attenuation <= lda[devid].MaxAttenuation))
        {
            old_element = lda[devid].Profile[ch][index];        // keep cached value in case IO fails
            lda[devid].Profile[ch][index] = attenuation;        // and stash the new value
			if (TestMode) return STATUS_OK;
        }
        else
            return STATUS_ERROR;

        // HiRes LDA
        VNX_command[3] = (BYTE)((index >> 8) & 0x03);       // the two ms bits of the index
        VNX_command[4] = (BYTE)index;                       // profile element to update
        VNX_command[0] = (BYTE)(attenuation & 0x000000ff);  // low byte of attenuation in .05 db device units
        VNX_command[1] = (BYTE)((attenuation & 0x0000ff00) >> 8);   // high byte of attenuation

        if (!SendReport(devid, VNX_SETPROFILE | VNX_SET, (char *)VNX_command, 5)) {
            lda[devid].Profile[ch][index] = old_element;
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Profile Count to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetProfileCount(char* deviceip, int profilecount) {
    int ch = 0;
    int old_profilecount, devid = 0;
    int max_count;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_profilecount = lda[devid].ProfileCount[ch];

        max_count = lda[devid].ProfileMaxLength;
        
        if (profilecount <= max_count && profilecount > 0)
		{ 
            lda[devid].ProfileCount[ch] = profilecount;
			if (TestMode) return STATUS_OK;
		}
		else
			return STATUS_ERROR;	// out of range count value
      
        // the profile count value is OK, lets send it to the hardware
        ptr = (char *) &lda[devid].ProfileCount[ch];

        if (!SendReport(devid, VNX_PROFILECOUNT | VNX_SET, ptr, 4)) {
            lda[devid].ProfileCount[ch] = old_profilecount;
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Profile Idle Time to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetProfileIdleTime(char* deviceip, int idletime) {
    int ch = 0;
    int old_idletime, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_idletime = lda[devid].ProfileIdleTime[ch];
        
        if (idletime >= VNX_MIN_DWELLTIME)
		{ 
            lda[devid].ProfileIdleTime[ch] = idletime;
			if(TestMode) return STATUS_OK;
		}
		else
			return STATUS_ERROR;
      
        // the profile idle time value is OK, lets send it to the hardware
        ptr = (char *) &lda[devid].ProfileIdleTime[ch];

        if (!SendReport(devid, VNX_PROFILEIDLE | VNX_SET, ptr, 4)) {
            lda[devid].ProfileIdleTime[ch] = old_idletime;
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Profile Dwell Time to the device
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SetProfileDwellTime(char* deviceip, int dwelltime) {
    int ch = 0;
    int old_dwelltime, devid = 0;
    char *ptr;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        old_dwelltime = lda[devid].ProfileDwellTime[ch];
        
        if (dwelltime >= VNX_MIN_DWELLTIME)
		{
            lda[devid].ProfileDwellTime[ch] = dwelltime;
			if (TestMode) return STATUS_OK;
		}
		else
			return STATUS_ERROR;
      
        // the profile dwell time value is OK, lets send it to the hardware
        ptr = (char *) &lda[devid].ProfileDwellTime[ch];

        if (!SendReport(devid, VNX_PROFILEDWELL | VNX_SET, ptr, 4)) {
            lda[devid].ProfileDwellTime[ch] = old_dwelltime;
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Set Profile mode to the device and start/stop profiles
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_StartProfile(char* deviceip, int mode) {
    int devid = 0;
    BYTE VNX_command[] = {0, 0, 0, 0, 0, 0};

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

        if (mode != 0)  // mode is 1 for a single profile, 2 for a repeating profile
        {
            VNX_command[0] = (BYTE) ((mode & 0x00000003) | STATUS_PROFILE_ACTIVE);  // start the profile
        }
        else
        {
            VNX_command[0] = 0;     // stop the profile
        }

		if (TestMode) return STATUS_OK;	// we don't emulate ramps or profiles in test mode yet

        if (!SendReport(devid, VNX_SWEEP | VNX_SET, (char *)VNX_command, 3)) {
            return STATUS_ERROR;
        }
    }
    else 
      return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Start Ramp Mode 
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_StartRamp(char* deviceip, bool go) {
    int icount;
    int ch = 0;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

		if (lda[devid].Channel < CHANNEL_MAX-1)
			ch = lda[devid].Channel;
		else 
			ch = CHANNEL_MAX-1;

        char VNX_ramp[] = {0, 0, 0, 0, 0, 0};

        if (go)
        {
            icount = 3;                                             // V2 and later LDA devices
            VNX_ramp[0] = (char) lda[devid].Modebits[ch] & 0x1f;    // support bi-directional ramps
        }
        else
        {
            icount = 3;
            VNX_ramp[0] = 0;
        }

		if (TestMode) return STATUS_OK;		// we don't simulate ramps in test mode

		if (DEBUG_OUT > 0) printf("Ramp Start: Devid: %d, Mode: %x\n",devid, VNX_ramp[0]);

        if (!SendReport(devid, VNX_SWEEP | VNX_SET, VNX_ramp, icount))
          return STATUS_ERROR;
    }
    else
        return STATUS_ERROR;
    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Start a ramp on multiple channels to the device 
// chmask has a 1 bit set for every channel to be started
// mode is composed of the sweep command byte flags SWP_DIRECTION, SWP_CONTINUOUS, SWP_ONCE, and SWP_BIDIR
// the deferred flag is not supported, but reserved for future use. Set it to false when calling
// this function for compatibility with future library versions
//
// RD 5/2024 experimental code
//*****************************************************************************
STATUS_REPORT_T fnLDA_StartRampMC(char* deviceip, int mode, int chmask, bool deferred)
{
    int bank, tmp;
    unsigned int ch, i;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

        char VNX_ramp[] = {0, 0, 0, 0, 0, 0};

        if (mode != 0){
            VNX_ramp[0] = (BYTE)(mode & MODE_SWEEP);    // mode sweep for V2 includes the 0x10 bit
        }
        else {
            VNX_ramp[0] = 0;
        }

		if (TestMode)
		{
			// we don't emulate ramps or profiles
			return STATUS_OK;
		}

		// limit the active channels based on the number of channels in the device
		tmp = 0xFFFFFFFF >> (32 - lda[devid].NumChannels);
		chmask &= tmp;								// clip off any extraneous select bits

		for (i = 0; i < 4; i++)
		{
			tmp = chmask >> ((8 * i) & 0xff);
			if (tmp != 0)
			{
				// we have channels selected in this bank
				VNX_ramp[4] = i + 1; 
        		VNX_ramp[5] = (BYTE)tmp;	// the channel bit mask for this bank

        		if (!SendReport(devid , VNX_SWEEP | VNX_SET, (char *)VNX_ramp, 6))
				{
            		return STATUS_ERROR;
        		}
			}
		}

    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to Start a profile on multiple channels to the device 
// chmask has a 1 bit set for every channel to be started
// mode is PROFILE_ONCE to play the profile once, or PROFILE_REPEAT to play the profile repeatedly.
// PROFILE_OFF stops the profile
// the deferred flag is not supported, but reserved for future use. Set it to false when calling
// this function for compatibility with future library versions
//
// RD 5/2024 experimental code
//*****************************************************************************
STATUS_REPORT_T fnLDA_StartProfileMC(char* deviceip, int mode, int chmask, bool deferred)
{
    int bank, tmp;
    unsigned int ch, i;
    int devid = 0;

    devid = GetDeviceID(deviceip);
    if (devid >= 0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

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

        if (mode != 0){
            VNX_command[0] = (BYTE) ((mode & 0x00000003) | STATUS_PROFILE_ACTIVE);  // start the profile 
        }
        else {
            VNX_command[0] = 0;
        }

		if (TestMode)
		{
			// we don't emulate ramps or profiles
			return STATUS_OK;
		}

		// limit the active channels based on the number of channels in the device
		tmp = 0xFFFFFFFF >> (32 - lda[devid].NumChannels);
		chmask &= tmp;								// clip off any extraneous select bits

		for (i = 0; i < 4; i++)
		{
			tmp = chmask >> ((8 * i) & 0xff);
			if (tmp != 0)
			{
				// we have channels selected in this bank

				VNX_command[4] = i + 1; 
        		VNX_command[5] = (BYTE)tmp;	// the channel bit mask for this bank

        		if (!SendReport(devid , VNX_SWEEP | VNX_SET, (char *)VNX_command, 6))
				{
            		return STATUS_ERROR;
        		}
			}
		}
    }
    else
        return STATUS_ERROR;

    return STATUS_OK;
}

//*****************************************************************************
//
// Function call to save user settings to flash on device 
//  
//*****************************************************************************
STATUS_REPORT_T fnLDA_SaveSettings(char* deviceip) {
    int devid=0;
    devid = GetDeviceID(deviceip);
    if( devid >=0)
    {
        // Check Device open or not
        if (!isDeviceSockOpen(deviceip))
            return STATUS_ERROR;

        // -- protect the LDA-602Q calibration tables on early devices
        if ((lda[devid].DevType == LDA_602Q) && (lda[devid].SerialNumber < 22160))
            return STATUS_ERROR;

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

        if (!SendReport(devid, VNX_SAVEPAR | VNX_SET, VNX_savesettings, 3))
            return STATUS_ERROR;
    }
    else
        return STATUS_ERROR;
  return STATUS_OK;
}
