ACQ2XX_API
acq2xx_api.cpp
Go to the documentation of this file.
00001 /* ------------------------------------------------------------------------- */
00002 /* file acq2xx_api.h                                                         */
00003 /* ------------------------------------------------------------------------- */
00004 /*   Copyright (C) 2008 Peter Milne, D-TACQ Solutions Ltd
00005  *                      <Peter dot Milne at D hyphen TACQ dot com>
00006 
00007  This program is free software; you can redistribute it and/or modify
00008  it under the terms of Version 2 of the GNU General Public License
00009  as published by the Free Software Foundation;
00010 
00011  This program is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with this program; if not, write to the Free Software
00018  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
00019 /* ------------------------------------------------------------------------- */
00020 
00021 /** @file acq2xx_api.cpp ACQ2xx C++ API Implementation. */
00022 
00023 #include "local.h"
00024 #include "acq_transport.h"
00025 #include "acq2xx_api.h"
00026 
00027 #define RETERR(rc) if (STATUS_ERR(rc)) return rc
00028 
00029 #define ERR_SCAN1       -101
00030 #define ERR_SCAN2       -102
00031 #define ERR_SCAN3       -103
00032 #define ERR_SCAN4       -104
00033 
00034 #define CMD     128
00035 #define REPLY   128
00036 
00037 #define ECASE(ev) case ev: return #ev;
00038 
00039 const char* toString(enum STATE s)
00040 {
00041         switch(s){
00042                 ECASE(ST_STOP); 
00043                 ECASE(ST_ARM);
00044                 ECASE(ST_RUN);
00045                 ECASE(ST_TRIGGER);
00046                 ECASE(ST_POSTPROCESS);
00047                 ECASE(ST_CAPDONE);
00048         default:
00049                 return "unknown";
00050         }
00051 }
00052 const char* toString(enum acq2xx__DOx dox)
00053 {
00054         switch(dox){
00055 
00056         case DO_NONE:   
00057                 return "DO_NONE";
00058         case DO0:
00059                 return "DO0";
00060         case DO1: 
00061                 return "DO1";
00062         case DO2: 
00063                 return "DO2";
00064         case DO3: 
00065                 return "DO3";
00066         case DO4:
00067                 return "DO4";
00068         case DO5:
00069                 return "DO5";
00070         default:
00071                 return "";
00072         }
00073 }
00074 
00075 const char* toString(enum acq2xx__EDGE edge)
00076 {
00077         return edge==EDGE_FALLING? "falling": "rising";
00078 }
00079 
00080 const char* toString(enum acq2xx__DIx dix)
00081 {
00082         switch(dix) {
00083         case DI_NONE:  
00084                 return "DI_NONE";
00085         case DI0:
00086                 return "DI0";
00087         case DI1:
00088                 return "DI1";
00089         case DI2:
00090                 return "DI2";
00091         case DI3:       
00092                 return "DI3";
00093         case DI4:
00094                 return "DI4";
00095         case DI5:
00096                 return "DI5";
00097         default:
00098                 return "";
00099         }
00100 }
00101 
00102 const char *toString(enum acq2xx_Dx dx)
00103 {
00104         switch(dx){
00105         case D0: return "d0";
00106         case D1: return "d1";
00107         case D2: return "d2";
00108         case D3: return "d3";
00109         case D4: return "d4";
00110         case D5: return "d5";
00111         default:
00112                 return "none";
00113         }
00114 }
00115 
00116 const char* toString(
00117         enum acq2xx_RoutePort port
00118         )
00119 {
00120         switch(port){
00121         case R_LEMO: return "lemo ";
00122         case R_FPGA: return "fpga ";
00123         case R_PXI:  return "pxi ";
00124         case R_RIO:  return "rio ";
00125         default:
00126                 return "";
00127         }
00128 }
00129 
00130 
00131 
00132 const char* toString(
00133         enum acq2xx_RoutePort port,
00134         enum acq2xx_RoutePort port2,
00135         enum acq2xx_RoutePort port3)
00136 {       
00137         static char str[80];
00138 
00139         strcpy(str, toString(port));
00140         strcat(str, toString(port2));
00141         strcat(str, toString(port3));
00142         return str;
00143 }
00144 
00145 
00146 Acq2xx::Acq2xx(Transport* _transport):
00147         transport(_transport), 
00148                 ranges(0),
00149                 ai(-1), ao(0)
00150 {
00151 
00152 }
00153 STATUS Acq2xx::setRoute(
00154         enum acq2xx_Dx dx,
00155         enum acq2xx_RoutePort in,
00156         enum acq2xx_RoutePort out,
00157         enum acq2xx_RoutePort out2,
00158         enum acq2xx_RoutePort out3)
00159 /**< make a signal routing (connection). 
00160  * "signal" is a digital control signal
00161  * @param dx - the signal
00162  * @param in - source: input port (one only)
00163  * @param out - sink: output port(s) 
00164  */
00165 {
00166         char reply[REPLY];
00167         char cmd[CMD];
00168 
00169         sprintf(cmd, "set.route %s in %s out %s\n",
00170                 toString(dx), toString(in), toString(out, out2, out3));
00171         
00172         int rc = transport->acq2sh(cmd, reply, REPLY);
00173         RETERR(rc);
00174 
00175         return STATUS_OK;
00176 }
00177 
00178 
00179 STATUS Acq2xx::setExternalClock(
00180         enum acq2xx__DIx dix, int div, enum acq2xx__DOx dox)
00181 /**< set External Clock definition.
00182  * @param dix - the signal
00183  * @param div - integer divide of dix
00184  * @param dox - optional output for divided signal
00185  */ 
00186 {
00187         return STATUS_WORKTODO;
00188 }
00189 
00190 STATUS Acq2xx::setInternalClock(
00191         int hz, enum acq2xx__DOx dox)
00192 /**< set Internal Clock defiition.
00193  * @param hz - frequency in Hz
00194  * @param dox - output the clock on this line
00195  */
00196 {
00197         char reply[REPLY];
00198         char cmd[CMD];
00199         
00200         if (dox != DO_NONE){
00201                 sprintf(cmd, "setInternalClock %d %s\n", hz, toString(dox));
00202         }else{
00203                 sprintf(cmd, "setInternalClock %d\n", hz);
00204         }
00205         int rc = transport->acqcmd(cmd, reply, REPLY);
00206         RETERR(rc);
00207 
00208         return STATUS_OK;
00209 }
00210 
00211 STATUS Acq2xx::getInternalClock(int& hz)
00212 /**< get the actual Internal Clock frequency.
00213  * @param hz - output value
00214  */
00215 {
00216         char reply[REPLY];
00217         int rc = transport->acqcmd("getInternalClock", reply, REPLY);
00218         RETERR(rc);
00219         int clock;
00220         if (sscanf(reply, "ACQ32:getInternalClock=%d", &clock) != 1){
00221                 return ERR_SCAN1;
00222         }
00223         hz = clock;
00224         return STATUS_OK;
00225 }
00226 
00227 STATUS Acq2xx::getAvailableChannels(int& maxChannels)
00228 /**< get the number of channels on the card.
00229  * @param maxChannels - output available channels.
00230  */
00231 {
00232         if (ai == -1){
00233                 char reply[REPLY];
00234         
00235                 int rc = transport->acqcmd("getAvailableChannels", 
00236                                                         reply, REPLY);
00237                 RETERR(rc);
00238                 if (sscanf(reply, "ACQ32:getAvailableChannels AI=%d AO=%d",
00239                            &ai, &ao) != 2){
00240 
00241                         return ERR_SCAN2;
00242                 }
00243         }
00244 
00245         maxChannels = ai;
00246         return STATUS_OK;
00247 }
00248 
00249 STATUS Acq2xx::selectChannels(const char* channelMask)
00250 /**< set the active channel mask.
00251  *   NB not all masks are valid, hardware will select nearest mask
00252  *   that includes all channels.
00253  * @param channelMask - mask selects active channels 1=> enabled
00254  */
00255 {
00256         char reply[REPLY];
00257         char cmd[CMD];
00258         
00259         sprintf(cmd, "setChannelMask %s\n", channelMask);
00260         int rc = transport->acqcmd(cmd, reply, REPLY);
00261         RETERR(rc);
00262 
00263         return STATUS_OK;
00264 }
00265 
00266 STATUS Acq2xx::getSelectedChannels(char* channelMask, int *count)
00267 /**< get the actual effective channel mask.
00268  * @param channelMask - outputs the actual mask
00269  * @param count - outputs selected channel count
00270  */
00271 {
00272         char reply[REPLY];
00273         int rc = transport->acqcmd("getChannelMask", reply, REPLY);
00274         RETERR(rc);
00275 
00276         if (sscanf(reply, "ACQ32:getChannelMask=%s", channelMask) != 1){
00277                 return ERR_SCAN1;
00278         }
00279         if (count != 0){
00280                 int n = 0;
00281                 for (int ic = 0; channelMask[ic]; ++ic){
00282                         if (channelMask[ic] == '1'){
00283                                 ++n;
00284                         }
00285                 }
00286                 *count = n;
00287         }
00288         return STATUS_OK;
00289 }
00290 
00291 STATUS Acq2xx::getChannelRanges(acq2xx_VRange * ranges, int maxRanges)
00292 /**< get a list of calibrated range values for each channel.
00293  * @param ranges - user allocated buffer to hold values.
00294  * @param maxRanges - length of user buffer
00295  */
00296 {
00297         char reply[4096];
00298         int rc = transport->acq2sh("get.vin\n", reply, 4096);
00299         RETERR(rc);
00300         
00301         char* cursor = reply;
00302         
00303         for(int ch = 1; ch <= maxRanges; ++ch){
00304                 int endc;
00305 
00306                 dbg(1, "scanning from [%d] len %d", 
00307                                 cursor-reply, strlen(cursor));
00308 
00309                 if (sscanf(cursor, "%f,%f,%n", 
00310                            &ranges[ch].vmin, &ranges[ch].vmax, &endc) >= 2){
00311 
00312                 }else{
00313                         err("failed at channel %d: \"%s\"", ch, cursor);
00314                         return ERR_SCAN2;
00315                 }
00316                 cursor += endc;
00317         }       
00318 
00319         return STATUS_OK;
00320 }
00321 
00322 STATUS Acq2xx::setPrePostMode(
00323         int prelen, int postlen, 
00324         enum acq2xx__DIx dix,
00325         enum acq2xx__EDGE edge)
00326 /**< configure the capture Mode.
00327  * @param prelen - number of samples before trigger
00328  * @param postlen - number of samples after trigger
00329  * @param dix - signal line for trigger
00330  * @param edge - sense of the signal
00331  */
00332 {
00333         char reply[REPLY];
00334         char cmd[CMD];
00335         const char* s_dix = dix==DI_NONE? "none": toString(dix);
00336         const char* s_edge = dix==DI_NONE? "": toString(edge);
00337 
00338         sprintf(cmd, "set.pre_post_mode %d %d %s %s\n",
00339                 prelen, postlen, s_dix, s_edge);
00340         int rc = transport->acq2sh(cmd, reply, REPLY);
00341         RETERR(rc);
00342         return STATUS_OK;
00343 }
00344 
00345 
00346 STATUS Acq2xx::setArm()
00347 /**< arm the card to start the capture. */
00348 {
00349         char reply[REPLY];
00350 
00351         int rc = transport->acqcmd("setArm\n", reply, REPLY);
00352         RETERR(rc);
00353         return STATUS_OK;       
00354 }
00355 
00356 STATUS Acq2xx::setAbort()
00357 /**< abort a capture */
00358 {
00359         char reply[REPLY];
00360 
00361         int rc = transport->acqcmd("setAbort\n", reply, REPLY);
00362         RETERR(rc);
00363         return STATUS_OK;
00364 }
00365 
00366 STATUS Acq2xx::getState(enum STATE& state)
00367 /**< output card state.
00368  * @param state - output state value.   
00369  */
00370 {
00371         char reply[REPLY];
00372         int _state;
00373 
00374         int rc = transport->acqcmd("getState\n", reply, REPLY);
00375         RETERR(rc);
00376         
00377         if (sscanf(reply, "ACQ32:%d ST_", &_state) != 1){
00378                 return ERR_SCAN1;
00379         }
00380         state = (enum STATE)_state;
00381         return STATUS_OK;
00382 
00383 }
00384         
00385 STATUS Acq2xx::waitState(enum STATE state, int timeout)
00386 /**< wait for selected State to occur, or timeout
00387  * @param state - state to wait for
00388  * @param timeout - timeout in msec
00389  */
00390 {
00391         return STATUS_WORKTODO;
00392 }
00393 
00394 STATUS Acq2xx::getNumSamples(int* total, int* pre, int* post, int *elapsed)
00395 /**< query current capture state.
00396  * @param total - output total samples
00397  * @param pre - output pre trigger samples
00398  * @param post - output post- trigger samples
00399  * @param elapsed - output samples since arm
00400  * NB: any param can be null, and is then ignored
00401  */
00402 {
00403         char response[128];
00404         int rc = transport->acqcmd("getNumSamples", response, sizeof(response));
00405         RETERR(rc);
00406         
00407         int _total, _pre, _post, _elapsed;
00408         
00409         if (sscanf(response, "ACQ32:getNumSamples=%d pre=%d post=%d elapsed=%d",
00410                    &_total, &_pre, &_post, &_elapsed) != 4){
00411                 err("failed to scan 4 values \"%s\"", response);
00412                 return -1;
00413         }
00414 
00415         if (total)      *total  = _total;
00416         if (pre)        *pre    = _pre;
00417         if (post)       *post   = _post;
00418         if (elapsed)    *elapsed= _elapsed;
00419 
00420         dbg(1, "SUCCESS %d %d %d %d", _total, _pre, _post, _elapsed);
00421 
00422         return STATUS_OK;
00423 }
00424                 
00425 STATUS Acq2xx::readChannel(int channel, short* data,
00426                    int nsamples, int start, int stride)
00427 /**< read and output raw data for channel
00428  * @param channel - channel number 1..N
00429  * @param data - caller's buffer
00430  * @param nsamples - max samples to read
00431  * @param start - start sample in data set
00432  * @param stride - stride [subsample] value
00433  * @returns actual samples returned or STATUS_ERR
00434  */
00435 {
00436         return transport->readChannel(channel, data, nsamples, start, stride);
00437 }
00438 
00439 STATUS Acq2xx::readChannelVolts(int channel, float* data_volts,
00440                         int nsamples, int start, int stride)
00441 /**< read and output calibrated data for channel
00442  * @param channel - channel number 1..N
00443  * @param data_volts - caller's buffer
00444  * @param nsamples - max samples to read
00445  * @param start - start sample in data set
00446  * @param stride - stride [subsample] value
00447  * @returns actual samples returned or STATUS_ERR
00448  */
00449 {
00450 
00451         int rc;
00452 
00453         if (ranges == 0){
00454                 int maxChannels;
00455                 rc = getAvailableChannels(maxChannels);
00456 
00457                 if (STATUS_ERR(rc)){
00458                         return rc;
00459                 }
00460                
00461                 ranges = new acq2xx_VRange[maxChannels+1]; /* index from 1 */
00462 
00463                 rc = getChannelRanges(ranges, maxChannels);             
00464                 if (STATUS_ERR(rc)){
00465                         return rc;
00466                 }
00467         }
00468         short* data = new short[nsamples];
00469         rc = readChannel(channel, data, nsamples, start, stride);
00470 
00471 /*
00472  * (v - v1)/(v2 - v1) = (r - r1)/(r2 - r1)
00473  * v = v1 + (r - r1)*(v2 - v1)/(r2 - r1)
00474  *
00475  * where r1 = -32768, r2 = 32767
00476  */
00477 #define R1 -32768
00478 #define R2 32767
00479 #define RR (R2 - R1)
00480 
00481         if (STATUS_IS_OK(rc)){
00482                 float vmin = ranges[channel].vmin;
00483                 float vmax = ranges[channel].vmax;
00484                 float vpeak = vmax - vmin;
00485         
00486                 for (int isam = 0; isam != rc; ++isam){
00487                         data_volts[isam] = vmin + (data[isam] - R1)*vpeak/RR;
00488                 }
00489         }
00490                 
00491         delete [] data; 
00492         return rc;
00493 }
00494 
00495 
00496 STATUS Acq2xx::readStreamingFrame(Frame* frame, unsigned id)
00497 /**< For streaming data, read the frame
00498  * @param frame - caller buffer to fill with data
00499  * @param id - previous frame # - id=0 means "start streaming"
00500  * @returns STATUS_OK or STATUS_ERR
00501  */
00502 {
00503         return transport->readStreamingFrame(frame, id);
00504 }
00505 
00506 STATUS Acq2xx::stopStreaming(void)
00507 /**< stops and clears up a previous streaming connection
00508  * @returns STATUS_OK or STATUS_ERR
00509  */
00510 {
00511         return transport->stopStreaming();
00512 }