/* program hpVecTrace
 * copies a trace of a waveform on the hp98440
 * to a file.
 * 5/16/95 Help from C. Saunders:
 * Put a rewind command in between all
 * groups of read and write operations to that socket.
 * don't put rewind within a group of like operation.
 */

#include "mdb.h"
#include "scan.h"
#include "match_string.h"
#include "SDDS.h"
#include <time.h>
#include <sys/timeb.h>
#include "lanio.h"
#include <memory.h>

#define HPVECBOO 0
#define HPVECSR 1
#define HPVECRF 2
#define HPVECPAR 3
#define HPVECS 4
char *hpvecAddress[HPVECS] = {
  "164.54.3.169",
  "164.54.2.68",
  "164.54.2.136"};
char *hpvecName[HPVECS] = {
  "hpvecboo.aps4.anl.gov",
  "hpvecsr.aps4.anl.gov",
  "hpvecrf.aps4.anl.gov",
  "hpvecpar.aps4.anl.gov"};

/* Note that positioning a "[" character in the first column of a
   string continued across many lines prevents emacs indenting
   feature from working. */
static char *USAGE = "hpVecTrace [-hpvecboo|-hpvecsr|-hpvecrf|-unit={sr|boo|rf}] \n\
       <tracename> <sddsfile>\n\
       -asciiprecision=<integer>,<integer>\n\
       -buffertransfer=<first-buffer-trace>,<last-buffer-trace> -verbose\n\
Gets traces from one of the hp vector signal analysers.\n\
-hpvecboo|-hpvecsr    sends commands to either the booster\n\
|-hpvecrf             VSA, the storage ring VSA, or the rf group's VSA.\n\
                      The default is the value of the environment\n\
                      variable HPVEC.\n\
-unit                 alternative way of specifying a HP VSA. The\n\
                      string hpvec is prepended to the string value given\n\
                      to create the VSA node name.\n\
<tracename>           valid tracenames are a, b, c, d, tcap and d1 through d6;\n\
<sddsfile>            sdds file containing column Frequency in Mhz, \n\
                      and Waveform in V;\n\
asciiprecision        Option is no longer recommended, since the default is\n\
                      binary transfer, which is faster;\n\
                      first value specifies the precision of the frequency,\n\
                      and the second value, if present, \n\
                      the precision of the Waveform;\n\
buffertransfer        transfer the trace buffer in the range given;\n\
                      if no range is given then all of the buffer is transfered;\n\
verbose               lists the HP-IB commands issued.\n\
Program by Louis Emery, ANL (Oct. 1994)\n";

#define BUFFER_LENGTH 512

#define CLO_VERBOSE 0
#define CLO_ASCII_PRECISION 1
#define CLO_BUFFER_TRANSFER 2
#define CLO_HPVECSR 3
#define CLO_HPVECBOO 4
#define CLO_HPVECRF 5
#define CLO_HPVECPAR 6
#define CLO_HPUNIT 7
#define COMMANDLINE_OPTIONS 8
char *commandline_option[COMMANDLINE_OPTIONS] = {
  "verbose",
  "asciiprecision",
  "buffertransfer",
  "hpvecsr",
  "hpvecboo",
  "hpvecrf",
  "hpvecpar",
  "unit"};

#define TRACEA 0
#define TRACEB 1
#define TRACEC 2
#define TRACED 3
#define REGISTER1 4
#define REGISTER2 5
#define REGISTER3 6
#define REGISTER4 7
#define REGISTER5 8
#define REGISTER6 9
#define TCAPTURE 10
#define NTRACES 11
char *tracenames[NTRACES] = {
  "a", "b", "c", "d", "d1", "d2", "d3", "d4", "d5", "d6", "tcap"};
long HPIBTraceNumber[4] = {1, 2, 3, 4}; /* array used to create HP-IB commands */

long verbose;

long openSocket(char *hostname, long portNumber);
char *queryInstrument(FILE *file, char *command, char *result, size_t maxLength);
long queryInstrumentBinary(FILE *file, char *command, double **array);
long commandInstrument(FILE *file, char *command);

int main(int argc, char **argv) {
  SCANNED_ARG *s_arg;
  long i, i_arg;
  long NumPoints, FreqNumPoints;
  char *outputfile;
  double *XAxisValues, *Waveform, *WaveformPower;
  char *TimeStamp;
  time_t start_time;
  long instrument;
  FILE *instFile;
  char *TraceID, *RegisterID;
  long TraceNo;
  char *InstrumentMode;
  long binarytransfer;
  long transferalltraces, firstbuffertrace, lastbuffertrace, buffertransfer;
  long firsttrace, lasttrace, BufferDepth, i_trace;
  char *unitName;

#define DEFAULT_ASCII_PRECISION 8

#define ASCIIPREC_FREQUENCY 0
#define ASCIIPREC_WAVEFORM 1
#define NUM_ASCIIPREC 2
  long ascii_precision[NUM_ASCIIPREC]; /* precision for frequency array and Waveform array */

  SDDS_TABLE output_table;

  char *command, *charBuf;
  char XUnits[32];
  char XAxisName[32];
  char OriginalDataMode[32];

  long buffer_length, length;
  double TimeRecordLength, PctOverlap, TimeOverlap;
  double TimePosition = 0, TimePositionIncrement, TimeCaptureAnalysisStart;
  long hpvec, spectogramMode;
  char *IPAddress;
  char *InstrumentName;
  double power;
  long outputPoints, firstValidIndex;

  buffer_length = BUFFER_LENGTH;
  charBuf = (char *)malloc(buffer_length * sizeof(char));
  command = (char *)malloc(buffer_length * sizeof(char));
  WaveformPower = Waveform = XAxisValues = NULL;

  argc = scanargs(&s_arg, argc, argv);
  if (argc < 3) {
    bomb(NULL, USAGE);
  }
  TraceID = NULL;
  RegisterID = (char *)malloc(buffer_length * sizeof(char));
  outputfile = NULL;
  for (i = 0; i < NUM_ASCIIPREC; i++)
    ascii_precision[i] = DEFAULT_ASCII_PRECISION;
  binarytransfer = 1;
  transferalltraces = 0;
  buffertransfer = 0;
  hpvec = -1;
  unitName = NULL;
  for (i_arg = 1; i_arg < argc; i_arg++) {
    if (s_arg[i_arg].arg_type == OPTION) {
      switch (match_string(s_arg[i_arg].list[0], commandline_option, COMMANDLINE_OPTIONS, UNIQUE_MATCH)) {
      case CLO_HPVECBOO:
        hpvec = HPVECBOO;
        break;
      case CLO_HPVECSR:
        hpvec = HPVECSR;
        break;
      case CLO_HPVECRF:
        hpvec = HPVECRF;
        break;
      case CLO_HPVECPAR:
        hpvec = HPVECPAR;
        break;
      case CLO_HPUNIT:
        if (s_arg[i_arg].n_items == 1) {
          bomb("No unit specified in option -unit.", USAGE);
        } else {
          unitName = (char *)malloc(sizeof(unitName) * (strlen(s_arg[i_arg].list[1]) + strlen("hpvec") + 1));
          strcat(strcpy(unitName, "hpvec"), s_arg[i_arg].list[1]);
        }
        break;
      case CLO_VERBOSE:
        verbose = 1;
        break;
      case CLO_BUFFER_TRANSFER:
        buffertransfer = 1;
        if (s_arg[i_arg].n_items == 1) {
          transferalltraces = 1;
        } else if (s_arg[i_arg].n_items == 3) {
          if (!get_long(&firstbuffertrace, s_arg[i_arg].list[1])) {
            bomb("invalid value given for option -buffertransfer", USAGE);
          }
          if (!get_long(&lastbuffertrace, s_arg[i_arg].list[2])) {
            bomb("invalid value given for option -buffertransfer", USAGE);
          }
          if (firstbuffertrace > lastbuffertrace)
            bomb("invalid syntax for option -buffertransfer.\n", USAGE);
        } else
          bomb("invalid syntax for option -buffertransfer.\n", USAGE);
        break;
      case CLO_ASCII_PRECISION:
        bomb("ascii option disabled", USAGE);
        if (s_arg[i_arg].n_items == 1)
          bomb("no value given for option -ascii_precision", USAGE);
        if (!get_long(&ascii_precision[ASCIIPREC_FREQUENCY], s_arg[i_arg].list[1])) {
          bomb("invalid value given for option -ascii_precision", USAGE);
        }
        if (s_arg[i_arg].n_items > 2) {
          if (!get_long(&ascii_precision[ASCIIPREC_WAVEFORM], s_arg[i_arg].list[2])) {
            bomb("invalid value given for option -ascii_precision", USAGE);
          }
        }
        binarytransfer = 0;
        break;
      default:
        bomb("unrecognized option given", USAGE);
      }
    } else {
      if (!TraceID) {
        TraceID = s_arg[i_arg].list[0];
        TraceID = str_tolower(TraceID);
      } else if (!outputfile)
        outputfile = s_arg[i_arg].list[0];
      else
        bomb("too many filenames given", USAGE);
    }
  }

  /*  check validity of trace specification */
  if (0 > (TraceNo = match_string(TraceID, tracenames, NTRACES, MATCH_WHOLE_STRING))) {
    fprintf(stderr, "Trace name %s not recognized\n", TraceID);
    exit(1);
  }

  /* check validity of hp vector analyser name */
  if (0 > hpvec) {
    if (unitName) {
      IPAddress = unitName;
    } else if (!(IPAddress = getenv("HPVEC"))) {
      fprintf(stderr, "No address option given, and environment variable HPVEC is undefined.\n");
      exit(1);
    }
  } else {
    IPAddress = (char *)malloc(sizeof(IPAddress) * (strlen(hpvecName[hpvec]) + 1));
    strcpy(IPAddress, hpvecName[hpvec]);
  }
  InstrumentName = (char *)malloc(sizeof(IPAddress) * (strlen(IPAddress) + 1));
  InstrumentName = strtok(IPAddress, ".");

  if ((instrument = openSocket(IPAddress, 5025)) < 0) {
    fprintf(stderr, "Problem opening socket 5025.\n");
    return 1;
  }

  if (!(instFile = fdopen(instrument, "r+"))) {
    fprintf(stderr, "Unable to create file * structure : %s\n",
            strerror(errno));
    return 1;
  }

  /* save trace to default register */
  switch (TraceNo) {
  case TRACEA:
  case TRACEB:
  case TRACEC:
  case TRACED:
    strcpy(RegisterID, "d6");
    sprintf(command, "trace:copy %s, trace%ld", RegisterID, HPIBTraceNumber[TraceNo]);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
    break;
  case TCAPTURE:
    /* save measurement data mode */
    sprintf(command, "calc:feed?");
    if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }
    strcpy(OriginalDataMode, charBuf + 1);
    length = strlen(OriginalDataMode);
    OriginalDataMode[length - 1] = 0;

    sprintf(command, "calc:feed 'tcap 1'");
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
    strcpy(RegisterID, "d6");
    sprintf(command, "trace:copy %s, trace1", RegisterID);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
    break;
  default:
    strcpy(RegisterID, TraceID);
    break;
  }

  /* get no. of points */
  sprintf(command, "trace:data:header:points? %s", RegisterID);
  if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
    fprintf(stderr, "Problem with query command: %s.\n", command);
    exit(1);
  }
  sscanf(charBuf, "%ld", &FreqNumPoints);
  if (!FreqNumPoints) {
    fprintf(stderr, "Something wrong with %s: No points.\n", RegisterID);
  }

  /* get the instrument mode */
  strcpy(command, "instrument:select?");
  if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
    fprintf(stderr, "Problem with query command: %s.\n", command);
    exit(1);
  }
  InstrumentMode = (char *)malloc((strlen(charBuf) + 1) * sizeof(char));
  strcpy(InstrumentMode, charBuf);

  /* specify ascii precision for frequency array */
  if (!binarytransfer) {
    sprintf(command, "format:data ascii,%ld", ascii_precision[ASCIIPREC_FREQUENCY]);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
  } else {
    /* specify real (binary) format */
    sprintf(command, "format:data real");
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
  }

  /* get x (frequency) data */
  sprintf(command, "trace:x:data? %s", RegisterID);
  if (!(FreqNumPoints = queryInstrumentBinary(instFile, command, &XAxisValues))) {
    fprintf(stderr, "Problem with query command: %s.\n", command);
    exit(1);
  }

#if defined(__linux__)
  if (!(instFile = fdopen(instrument, "r+"))) {
    fprintf(stderr, "Unable to create file * structure : %s\n",
            strerror(errno));
    return 1;
  }
#endif

  sprintf(command, "trace:x:unit? %s", RegisterID);
  if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
    fprintf(stderr, "Problem with query command: %s.\n", command);
    exit(1);
  }
  strcpy(XUnits, charBuf + 1);
  length = strlen(XUnits);
  XUnits[length - 1] = 0;
  if (!strcasecmp(XUnits, "Hz")) {
    strcpy(XUnits, "MHz");
    for (i = 0; i < FreqNumPoints; i++) {
      XAxisValues[i] /= 1e6;
    }
  }

  time(&start_time);
  TimeStamp = ctime(&start_time);
  TimeStamp[strlen(TimeStamp) - 1] = 0; /* kill newline */
  if (!SDDS_InitializeOutput(&output_table, SDDS_BINARY, 1,
                             "HP89440A Waveform", "HP89440A Waveform", outputfile))
    SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (0 > SDDS_DefineParameter(&output_table, "TimeStamp", "TimeStamp",
                               NULL, "TimeStamp", NULL, SDDS_STRING, NULL) ||
      0 > SDDS_DefineParameter(&output_table, "Time", "Time",
                               NULL, "Time", NULL, SDDS_DOUBLE, NULL) ||
      0 > SDDS_DefineParameter(&output_table, "InstrumentName", "InstrumentName",
                               NULL, "InstrumentName", NULL, SDDS_STRING, NULL))
    SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  /* define parameters and their string versions */
  if (buffertransfer) {
    if (0 > SDDS_DefineParameter(&output_table, "BufferDepth", "BufferDepth", NULL, "BufferDepth",
                                 NULL, SDDS_LONG, NULL) ||
        0 > SDDS_DefineParameter(&output_table, "TraceNumber", "TraceNumber", NULL, "TraceNumber",
                                 NULL, SDDS_LONG, NULL) ||
        0 > SDDS_DefineParameter(&output_table, "TimePosition", "TimePosition", "ms", "TimePosition",
                                 NULL, SDDS_DOUBLE, NULL) ||
        0 > SDDS_DefineParameter(&output_table, "BufferDepthString", "BufferDepth", NULL, "String version of BufferDepth",
                                 NULL, SDDS_STRING, NULL) ||
        0 > SDDS_DefineParameter(&output_table, "TraceNumberString", "TraceNumber", NULL, "String version of TraceNumber",
                                 NULL, SDDS_STRING, NULL) ||
        0 > SDDS_DefineParameter(&output_table, "TimePositionString", "TimePosition", NULL, "String version of TimePosition",
                                 NULL, SDDS_STRING, NULL))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  }
  /* determine which units and column name to use in the sdds file */
  if (!strcasecmp(XUnits, "MHz")) {
    strcpy(XAxisName, "Frequency");
    if (0 > SDDS_DefineColumn(&output_table, XAxisName, "f", XUnits, XAxisName, NULL, SDDS_DOUBLE, 0) ||
        0 > SDDS_DefineColumn(&output_table, "Waveform", "Waveform", "V", "Waveform in linear units", NULL, SDDS_DOUBLE, 0) ||
        0 > SDDS_DefineColumn(&output_table, "WaveformPower", "Waveform", "dBm", "Waveform in power units", NULL, SDDS_DOUBLE, 0) ||
        !SDDS_WriteLayout(&output_table))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  } else {
    strcpy(XAxisName, "Time");
    if (0 > SDDS_DefineColumn(&output_table, XAxisName, "t", XUnits, XAxisName, NULL, SDDS_DOUBLE, 0) ||
        0 > SDDS_DefineColumn(&output_table, "Waveform", "Waveform", "V", "Waveform in linear units", NULL, SDDS_DOUBLE, 0) ||
        0 > SDDS_DefineColumn(&output_table, "WaveformPower", "Waveform", "dBm", "Waveform in power units", NULL, SDDS_DOUBLE, 0) ||
        !SDDS_WriteLayout(&output_table))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  }
  if (verbose)
    printf("Defining columns %s and %s...\n", XAxisName, "Waveform");

  if (buffertransfer) {
    sprintf(command, "calc%1ld:marker:trace on", HPIBTraceNumber[TraceNo]);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
    if (transferalltraces) {
      firsttrace = 1;
      /* find buffer depth */
      sprintf(command, "display:window%1ld:trace:buff?", HPIBTraceNumber[TraceNo]);
      if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
        fprintf(stderr, "Problem with query command: %s.\n", command);
        exit(1);
      }
      sscanf(charBuf, "%ld", &BufferDepth);
      lasttrace = BufferDepth;
      if (verbose)
        fprintf(stderr, "Buffer depth is found to be %ld.\n", BufferDepth);
    } else {
      firsttrace = firstbuffertrace;
      lasttrace = lastbuffertrace;
    }
    /* Determine time record length */
    sprintf(command, "sense:sweep:time:span?");
    if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }
    sscanf(charBuf, "%lf", &TimeRecordLength);
    /* Determine overlap between consecutive time blocks (time records) */
    sprintf(command, "sense:sweep:time:overlap?");
    if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }
    sscanf(charBuf, "%lf", &PctOverlap);
    TimeOverlap = TimeRecordLength / FreqNumPoints * floor(FreqNumPoints * PctOverlap / 100);
    TimePositionIncrement = TimeRecordLength - TimeOverlap;
    /* starting time position */
    sprintf(command, "sense:tcapture:start?");
    if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }
    sscanf(charBuf, "%lf", &TimeCaptureAnalysisStart);
    /* select first trace */
    sprintf(command, "calc%1ld:marker:z %ld", HPIBTraceNumber[TraceNo], firsttrace);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
    /* check playback direction */
    sprintf(command, "sense:tcapture:direction?");
    if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }
    if (!strcasecmp(charBuf, "REVERSE")) {
      TimePositionIncrement *= -1;
      fprintf(stderr, "Warning: playback direction is reverse.\n "
              "Time position parameter appearing in the output file is invalid.\n");
    }
  }

  /* In this we're just transfering one trace */
  if (!buffertransfer) {
    /* get complex Waveform data */
    sprintf(command, "trace:data? %s", RegisterID);
    if (!(NumPoints = queryInstrumentBinary(instFile, command, &Waveform))) {
      fprintf(stderr, "Problem with query command: %s.\n", command);
      exit(1);
    }

    /* rearrange Waveform array if necessary */
    WaveformPower = (double *)realloc(WaveformPower, NumPoints * sizeof(double));
    if (2 * FreqNumPoints == NumPoints) {
      if (!strcasecmp(XUnits, "MHz")) {
        for (i = 0; i < FreqNumPoints; i++) {
          Waveform[i] = sqrt(sqr(Waveform[2 * i]) + sqr(Waveform[2 * i + 1]));
          WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
        }
      } else {
        for (i = 0; i < FreqNumPoints; i++) {
          Waveform[i] = Waveform[2 * i];
          WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
        }
      }
    } else {
      for (i = 0; i < NumPoints; i++) {
        WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
      }
    }

    /* chop off edges of spectrum */
    power = log((double)FreqNumPoints) / log(2.0);
    outputPoints = 50 * pow(2.0, (power - 6)) + 1;
    firstValidIndex = 7 * pow(2.0, (power - 6));

    if (!SDDS_StartTable(&output_table, FreqNumPoints) ||
        !SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                            "TimeStamp", TimeStamp, NULL) ||
        !SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                            "Time", (float)start_time, NULL) ||
        !SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                            "InstrumentName", InstrumentName, NULL) ||
        !SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                        &XAxisValues[firstValidIndex], outputPoints, XAxisName) ||
        !SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                        &Waveform[firstValidIndex], outputPoints, "Waveform") ||
        !SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                        &WaveformPower[firstValidIndex], outputPoints, "WaveformPower") ||
        !SDDS_WriteTable(&output_table))
      SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  } else {
    /* buffer transfer */
    for (i_trace = firsttrace; i_trace <= lasttrace; i_trace++) {
      /* check that instrument is in spectogram or waterfall mode */
      sprintf(command, "disp:wind1:spec?");
      if (!queryInstrument(instFile, command, charBuf, buffer_length)) {
        fprintf(stderr, "Problem with query command: %s.\n", command);
        exit(1);
      }
      sscanf(charBuf, "%ld", &spectogramMode);
      if (!spectogramMode) {
        fprintf(stderr, "HP VSA display window not in spectograph mode.\n");
        exit(1);
      }

      /* select trace and save to register, if necessary */
      if (i_trace != firsttrace) {
        sprintf(command, "calc%1ld:marker:z %ld", HPIBTraceNumber[TraceNo], i_trace);
        if (!commandInstrument(instFile, command)) {
          fprintf(stderr, "Problem with command: %s.\n", command);
          exit(1);
        }
        TimePosition += TimePositionIncrement;
      } else {
        TimePosition = TimeCaptureAnalysisStart + (i_trace - 1) * TimePositionIncrement;
      }
      /* save trace to default register */
      switch (TraceNo) {
      case TRACEA:
      case TRACEB:
      case TRACEC:
      case TRACED:
        sprintf(command, "trace:copy d6, trace%ld", HPIBTraceNumber[TraceNo]);
        strcpy(RegisterID, "d6");
        break;
      default:
        strcpy(RegisterID, TraceID);
        break;
      }
      if (!commandInstrument(instFile, command)) {
        fprintf(stderr, "Problem with command: %s.\n", command);
        exit(1);
      }

      /* get complex Waveform data */
      sprintf(command, "trace:data? %s", RegisterID);
      if (!(NumPoints = queryInstrumentBinary(instFile, command, &Waveform))) {
        fprintf(stderr, "Problem with query command: %s.\n", command);
        exit(1);
      }

      /* rearrange Waveform array if necessary */
      WaveformPower = (double *)realloc(WaveformPower, NumPoints * sizeof(double));
      if (2 * FreqNumPoints == NumPoints) {
        if (!strcasecmp(XUnits, "MHz")) {
          for (i = 0; i < FreqNumPoints; i++) {
            Waveform[i] = sqrt(sqr(Waveform[2 * i]) + sqr(Waveform[2 * i + 1]));
            WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
          }
        } else { /* x-axis is time */
          for (i = 0; i < FreqNumPoints; i++) {
            Waveform[i] = Waveform[2 * i];
            WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
            XAxisValues[i] = (i_trace - 1) * TimePositionIncrement;
          }
        }
      } else {
        for (i = 0; i < NumPoints; i++) {
          WaveformPower[i] = 20.0 * (log10(Waveform[i]) + 0.5);
        }
      }

      if (!SDDS_StartTable(&output_table, NumPoints) ||
          !SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                              "TimeStamp", TimeStamp,
                              "Time", (float)start_time,
                              "BufferDepth", BufferDepth,
                              "TraceNumber", i_trace,
                              "TimePosition", TimePosition * 1000,
                              NULL))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      /* these calls have to be separate because the value of charBuf returned from sprintf are not
         guaranteed to be valid when sprintf is repeated in the argument list */
      sprintf(charBuf, "%ld", i_trace);
      if (!SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                              "TraceNumberString", charBuf,
                              NULL))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      sprintf(charBuf, "%ld", BufferDepth);
      if (!SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                              "BufferDepthString", charBuf,
                              NULL))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
      sprintf(charBuf, "%g ms", TimePosition * 1000);
      if (!SDDS_SetParameters(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
                              "TimePositionString", charBuf,
                              NULL))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);

      /* chop off edges of spectrum */
      power = log((double)FreqNumPoints) / log(2.0);
      outputPoints = 50 * pow(2.0, (power - 6)) + 1;
      firstValidIndex = 7 * pow(2.0, (power - 6));

      if (!SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                          &XAxisValues[firstValidIndex], outputPoints, XAxisName) ||
          !SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                          &Waveform[firstValidIndex], outputPoints, "Waveform") ||
          !SDDS_SetColumn(&output_table, SDDS_SET_BY_NAME | SDDS_PASS_BY_REFERENCE,
                          &WaveformPower[firstValidIndex], outputPoints, "WaveformPower") ||
          !SDDS_WriteTable(&output_table))
        SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
    }
  }

  if (!SDDS_Terminate(&output_table))
    SDDS_PrintErrors(stdout, SDDS_VERBOSE_PrintErrors | SDDS_EXIT_PrintErrors);
  if (binarytransfer) {
    sprintf(command, "format:data ascii");
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
  }

  if (TraceNo == TCAPTURE) {
    sprintf(command, "calc:feed '%s'", OriginalDataMode);
    if (!commandInstrument(instFile, command)) {
      fprintf(stderr, "Problem with command: %s.\n", command);
      exit(1);
    }
  }

  exit(0);
}

long openSocket(char *hostname, long portNumber) {
  struct hostent *hostPtr;

  struct sockaddr_in peeraddr_in;
  long s;

  memset((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));
  /*    {
        long address;
        address = inet_addr((char*)hostname);
        hostPtr=gethostbyaddr((char*)&address,4,0);
        }
  */
  hostPtr = gethostbyname((char *)hostname);

  if (hostPtr == NULL) {
    fprintf(stderr, "unable to resolve hostname '%s'\n", hostname);
    return (-1);
  }
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s == -1) {
    fprintf(stderr, "unable to create socket to '%s': %s\n",
            hostname, strerror(errno));
    return (-1);
  }

  memcpy((char *)&peeraddr_in.sin_addr.s_addr, hostPtr->h_addr, hostPtr->h_length);
  peeraddr_in.sin_family = AF_INET;
  peeraddr_in.sin_port = htons(portNumber);
  if (connect(s, (struct sockaddr *)&peeraddr_in, sizeof(struct sockaddr_in)) == -1) {
    fprintf(stderr, "unable to create socket to '%s': %s\n",
            hostname, strerror(errno));
    return (-1);
  }
  return s;
}

char *queryInstrument(FILE *file, char *command, char *result, size_t maxLength) {
  long length;

  if (verbose) {
    printf("%s\n", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0) {
    fprintf(stderr, "Unable to send command \"%s\"\n", command);
    return NULL;
  }

  fflush(file);
  rewind(file);

  if (fgets(result, maxLength, file) == NULL) {
    fprintf(stderr, "fgets returns NULL for command \"%s\"\n", command);
    return NULL;
  }
  /*fflush(file);*/
  rewind(file);

  length = strlen(result);
  if (result[length - 1] == '\n')
    result[length - 1] = '\0';
  if (verbose) {
    if (length < 100) {
      printf("%s\n", result);
    } else {
      printf("Results has more than 100 characters\n");
    }
    fflush(stdout);
  }
  return (result);
}

/* query the instrument for an array of binary doubles */
/* also returns the number of points */
long queryInstrumentBinary(FILE *file, char *command, double **array) {
  long ndigits, nitems, idigits, i;

  if (verbose) {
    printf("%s\n", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0)
    return 0;
  fflush(file);
  rewind(file);

  getc(file);
  ndigits = getc(file) - '0';
  nitems = 0;
  for (idigits = 0; idigits < ndigits; idigits++) {
    nitems = getc(file) - '0' + 10 * nitems;
  }
  nitems /= 8;

  if (!*array) {
    *array = (double *)malloc(nitems * sizeof(double));
  } else {
    *array = (double *)realloc(*array, nitems * sizeof(double));
  }
  if (fread(*array, 8, nitems, file) != nitems) {
    fprintf(stderr, "something wrong with binary read of array.\n");
    return 0;
  }
  if (!SDDS_IsBigEndianMachine()) {
    for (i = 0; i < nitems; i++) {
      SDDS_SwapDouble(&((*array)[i]));
    }
  }

  /*fflush(file);*/
  rewind(file);

  return (nitems);
}

long commandInstrument(FILE *file, char *command) {
  if (verbose) {
    printf("%s\n", command);
    fflush(stdout);
  }

  if (fprintf(file, "%s\n", command) < 0)
    return 0;
  fflush(file);
  rewind(file);

  return 1;
}
