ACQ2XX_API
AcqDataModel.cpp
Go to the documentation of this file.
00001 /* ------------------------------------------------------------------------- */
00002 /* file AcqDataModel.cpp                                                     */
00003 /* ------------------------------------------------------------------------- */
00004 /*   Copyright (C) 2010 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 AcqDataModel.cpp implementation of Data Model 
00022  * Refs: 
00023 */
00024 
00025 
00026 using namespace std;
00027 
00028 #include "local.h"
00029 #include <vector>
00030 #include <string>
00031 
00032 #include "AcqDataModel.h"
00033 #include "AcqType.h"
00034 
00035 #include "Acq132.h"
00036 
00037 
00038 #include <time.h>
00039 #include <unistd.h>
00040 
00041 
00042 /** @todo: handle larger buffers if required */
00043 #define MAXBUF  0x600000
00044 
00045 template <class T>
00046 class LinearDataModel :public AcqDataModel {
00047         vector<NewEventSignature*> eventSignatures;
00048 protected:
00049         int *raw_lut;
00050         vector<vector<T>* > cooked;     /* index lchan order, starting 1 */
00051         virtual void dumpTimebase(DumpDef& dd);
00052 
00053 private:        
00054         void build_raw_lut(void);
00055         void allocate_cooked(void);
00056 public:
00057         LinearDataModel(const AcqType& _acq_type, 
00058                         string _scanlist, string _channel_mask):
00059                 AcqDataModel(_acq_type, _scanlist, _channel_mask)
00060         {
00061                 build_raw_lut();
00062                 allocate_cooked();
00063         }
00064         virtual ~LinearDataModel();
00065 
00066         virtual void print();
00067         virtual void processRaw(void *data, int ndata_bytes);
00068         virtual void processCooked(const void *cdata, int ch, int ndata_bytes);
00069         virtual void dump(DumpDef& dd);
00070 
00071         virtual void clear();
00072 
00073         virtual void addEventSignature(NewEventSignature* es) {
00074                 eventSignatures.push_back(es);
00075         }
00076         /** @@todo major plumbing required! */
00077         virtual vector<short>& getChannelData(int ch);
00078 
00079         virtual vector<NewEventSignature*>& getEventSignatures() {
00080                 return eventSignatures;
00081         }
00082 };
00083 
00084 /* specialized template works for shorts. ints can come later */
00085 template <>
00086 vector<short>& LinearDataModel<short>::getChannelData(int ch) {
00087         return *cooked[ch];
00088 }
00089 
00090 template <class T>
00091 vector<short>& LinearDataModel<T>::getChannelData(int ch) {
00092         return AcqDataModel::getChannelData(ch);
00093 }
00094 
00095 // @@todo: only default scanlist, channel_mask supported
00096 class Acq132DataModelAdapter: public AcqDataModel {
00097         Acq132DataModel acq132;
00098 public:
00099         Acq132DataModelAdapter(const AcqType& _acq_type, 
00100                             string _scanlist, string _channel_mask):
00101                 AcqDataModel(_acq_type, _scanlist, _channel_mask),
00102 // @@todo .. deliberately pick default scanlist, channel mask at this point
00103                         acq132(_acq_type.model)
00104         {
00105         
00106         }       
00107         virtual void print() {
00108                 acq132.print(); 
00109         }
00110         virtual void processRaw(void *data, int ndata_bytes)
00111         {
00112                 /* sizeof(short) forces unsigned -
00113                  * remember ndata_bytes "cleverly" encodes NO ES
00114                  * on negative values
00115                  */
00116                 acq132.process((short*)data, ndata_bytes/2);
00117         }
00118         virtual void dump(DumpDef& dd) {
00119                 dbg(1, "dump \"%s\" fullSet %d",
00120                                         dd.root.c_str(), dd.specifiesFullSet());
00121                 if (dd.specifiesFullSet()){
00122                         acq132.dump(dd.root, dd.event_sample_start);
00123                         if (!dd.appending){
00124                                 dumpFormat(dd.root);
00125                         }
00126                 }else{
00127                         acq132.dump(dd);
00128                 }
00129 
00130                 dbg(1, "99");
00131         }
00132         virtual void visitChannels(ChannelVisitor& visitor) {
00133                 // @@todo hardcode alert
00134 
00135                 dbg(1, "01: channels HARDCODE 32");
00136 
00137                 for (int ichan = 1; ichan !=32; ++ichan){
00138                         const vector<short>& v(acq132.getChannel(ichan));
00139                         vector<short>::const_iterator it;
00140                         for (it = v.begin(); it != v.end(); ++it){
00141                                 visitor.onSample(ichan, *it);
00142                         }
00143                 }
00144         }
00145         virtual vector<int>& getEvents() {
00146                 dbg(1, "impl size %d", acq132.getEvents().size());
00147                 return acq132.getEvents();
00148         }
00149         virtual vector<short>& getChannelData(int ch) {
00150                 return acq132.getChannel(ch);
00151         }
00152         virtual void clear() {
00153                 acq132.clear();
00154         }
00155         virtual void setWallClockPolicy(unsigned msecs_start){
00156                 AcqDataModel::setWallClockPolicy(msecs_start);
00157                 acq132.setWallClockPolicy(msecs_start, true);
00158         }
00159         virtual void setMaxsamples(int _maxsamples) {
00160                 acq132.setMaxsamples(_maxsamples);
00161         }
00162 };
00163 
00164 
00165 
00166 void AcqDataModel::print(void)
00167 {
00168         printf("AcqDataModel::print()\n");      
00169 }
00170 
00171 void AcqDataModel::clear(int expected_samples)
00172 {
00173         printf("AcqDataModel::clear()\n");
00174 }
00175 void AcqDataModel::processRaw(void *data, int ndata_bytes)
00176 {
00177         printf("AcqDataModel::processRaw(%p, %d)\n", data, ndata_bytes);
00178 }
00179 
00180 void AcqDataModel::processCooked(const void *cdata, int ch, int ndata_bytes)
00181 {
00182         fprintf(stderr, "ERROR:AcqDataModel::processCooked(%p, %d, %d)\n",
00183                 cdata, ch, ndata_bytes);
00184 }
00185 
00186 string AcqDataModel::pfx;
00187 
00188 void AcqDataModel::dumpFormat(const string& dirFile, unsigned long start_sample)
00189 {
00190         dbg(1, "dirFile:%s", dirFile.c_str());
00191         File format(dirFile, "format", "w");
00192         char fmt = acq_type.getWordSize() == 2? 's': 'S';
00193         double gain, offset;
00194         int ch;
00195 
00196         fprintf(format.getFp(), 
00197                 "# format for model:%s nchan:%d word_size:%d\n", 
00198                 acq_type.model.c_str(),
00199                 acq_type.nchan,
00200                 acq_type.word_size);
00201 
00202         ;
00203 
00204         fprintf(format.getFp(), "# ");
00205         for (string::iterator it = ident.begin(); it != ident.end(); ++it){
00206                 fprintf(format.getFp(), "%c", *it);
00207                 if (*it == '\n'){
00208                         fprintf(format.getFp(), "# ");  
00209                 }
00210         }
00211         fprintf(format.getFp(), "\n");
00212         fprintf(format.getFp(), "# DATASOURCE\t%s\n", dataSourceName.c_str());
00213         {
00214                 char tbuf[80];
00215                 struct tm tm;
00216 
00217                 gethostname(tbuf, 80);
00218                 fprintf(format.getFp(), "# HOSTNAME\t%s\n", tbuf);
00219 
00220                 time_t tt = time(0);
00221 
00222                 fprintf(format.getFp(), "# CREATED\t%s\n",
00223                                 asctime_r(localtime_r(&tt, &tm), tbuf));
00224 
00225 
00226         }
00227 
00228         char pfx_path[128];
00229         if (strlen(pfx.c_str())){
00230                 strcpy(pfx_path, pfx.c_str());
00231                 //sprintf(pfx_path, "%s.", pfx.c_str());
00232         }else{
00233                 pfx_path[0] = '\0';
00234         }
00235 
00236         unsigned start_sample_by_wallclock =
00237                 (unsigned)(msecs_start * (1000000/Clock::sample_clock_ns));
00238 
00239         if (wallclock_policy != WCP_TIMED_AT_EVENT){
00240                 start_sample_by_wallclock += start_sample;
00241         }
00242         fprintf(format.getFp(), "%sSTART_SAMPLE CONST UINT32 %lu\n",
00243                         pfx_path, start_sample_by_wallclock);
00244 
00245         for (ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00246 // AI01 RAW     S       1
00247                 fprintf(format.getFp(), "%s%s%02d\tRAW\t%c\t1\n",
00248                                 pfx_path, ch_name_core, ch, fmt);
00249         }
00250         for (ch = 1; has_timebase && ch <= acq_type.getNumChannels(); ++ch){
00251 // AI01 RAW     S       1
00252                 fprintf(format.getFp(), "%sTB%02d\tRAW\td\t1\n",
00253                                 pfx_path, ch, fmt);
00254 
00255                 if (DumpDef::common_timebase){
00256                         break;
00257                 }
00258         }
00259         for (ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00260 // AI01 RAW     S       1
00261                 acq_cal->getCal(ch, gain, offset);
00262                 fprintf(format.getFp(), "%sV%02d\tLINCOM\t1\t%s%s%02d\t%g\t%g\n",
00263                                 pfx_path, ch,
00264                                 pfx_path, ch_name_core, ch, gain, offset);
00265         }
00266 }
00267 
00268 template <class T>
00269 LinearDataModel<T>::~LinearDataModel()
00270 {
00271         for (int ch = 1;  ch <= acq_type.getNumChannels(); ++ch){
00272                 if (cooked[ch]){
00273                         delete cooked[ch];
00274                 }
00275         }
00276 
00277         delete [] raw_lut;
00278 }
00279 
00280 template <class T>
00281 void LinearDataModel<T>::build_raw_lut(void)
00282 {
00283         raw_lut = new int[acq_type.getNumChannels()];
00284 
00285         for (int ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00286                 raw_lut[acq_type.getChannelOffset(ch)] = ch;
00287         }
00288 }
00289 template <class T>
00290 void LinearDataModel<T>::allocate_cooked(void)
00291 {
00292         cooked.push_back(0);            /* index lchan order, starting 1 */     
00293         for (int ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00294                 cooked.push_back(new vector<T>);
00295         }
00296 }
00297 
00298 
00299 template <class T>
00300 void LinearDataModel<T>::clear()
00301 {
00302         for (int ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00303                 cooked[ch]->clear();
00304         }
00305 }
00306 
00307 template <class T>
00308 void LinearDataModel<T>::print(void)
00309 {
00310         printf("LinearDataModel::print()\n");   
00311 }
00312 
00313 
00314 template <class T>
00315 void LinearDataModel<T>::processRaw(void *data, int ndata_bytes)
00316 {
00317         dbg(2, "LinearDataModel::processRaw(%p, %d)\n", data, ndata_bytes);
00318 
00319         T* pt = (T*)data;
00320         int nt = ndata_bytes/sizeof(T);
00321         int ch_samples = nt/acq_type.getNumChannels();
00322         unsigned long sample_cursor = 0;
00323 
00324         dbg(3, "%08x %08x %08x %08x", pt[0], pt[1], pt[2], pt[3]);
00325 
00326         for (int ch = 1; ch < acq_type.getNumChannels(); ++ch){
00327                 cooked[ch]->reserve(cooked[ch]->size()+ch_samples);
00328         }
00329 
00330         for (int iraw = 0; nt > 0; nt--){
00331                 NewEventSignature *new_es;
00332                 if ((new_es = acq_type.createES((short *)pt, sample_cursor)) != 0){
00333                         pt += acq_type.getNumChannels();
00334                         addEventSignature(new_es);
00335                         continue;
00336                 }else
00337 
00338                         cooked[raw_lut[iraw]]->push_back(*pt++);
00339                 if (++iraw >= acq_type.getNumChannels()){
00340                         iraw = 0;
00341                         sample_cursor++;
00342                 }
00343         }
00344 }
00345 
00346 template <class T>
00347 void LinearDataModel<T>::processCooked(
00348                 const void *cdata, int ch, int ndata_bytes)
00349 {
00350         T* pt = (T*)cdata;
00351         int nt = ndata_bytes/sizeof(T);
00352         int ch_samples = nt;
00353 
00354 
00355         dbg(1, "%08x %08x %08x %08x", pt[0], pt[1], pt[2], pt[3]);
00356 
00357         cooked[ch]->reserve(cooked[ch]->size()+ch_samples);
00358 
00359         for (; nt > 0; nt--){
00360                 cooked[ch]->push_back(*pt++);
00361         }
00362 }
00363 
00364 template <class T>
00365 void LinearDataModel<T>::dumpTimebase(DumpDef& dd)
00366 {
00367         {
00368                 vector<NewEventSignature*>::iterator it = eventSignatures.begin();
00369                 for (int ix = 0; it != eventSignatures.end(); ++it, ++ix){
00370                         printf("ES %5d cursor:%10d time %.6f\n",
00371                                         ix, (*it)->getSampleCursor(),
00372                                         (*it)->timeInSeconds());
00373                 }
00374         }
00375         unsigned int sample_max = cooked[1]->size();
00376         const double es_isi = Clock::sample_clock_ns*1e-9;
00377         vector<NewEventSignature*>::iterator it = eventSignatures.begin();
00378         unsigned int sample_cursor = 0;
00379 
00380         if ((*it)->getSampleCursor() != 0){
00381                 err("unable to create timebase unless es at sample 0");
00382                 return;
00383         }
00384 
00385         double t1 = (*it)->timeInSeconds();
00386         dbg(1, "timebase start %e len %d", t1, sample_max);
00387 
00388         char tb_name[256];
00389         sprintf(tb_name, "%sTB01", AcqDataModel::pfx.c_str());
00390 
00391         File f(dd.root, tb_name);
00392 
00393         for (; it != eventSignatures.end(); ++it){
00394                 t1 = (*it)->timeInSeconds();
00395 
00396                 while (sample_cursor++ < (*it)->getSampleCursor()){
00397                         fwrite(&t1, sizeof(double), 1, f.getFp());
00398                         t1 += es_isi;
00399                 }
00400         }
00401 
00402         while (sample_cursor++ < sample_max){
00403                 fwrite(&t1, sizeof(double), 1, f.getFp());
00404                 t1 += es_isi;
00405         }
00406 }
00407 
00408 template <class T>
00409 void LinearDataModel<T>::dump(DumpDef& dd)
00410 {
00411         /** @@todo - handle pre/post. */
00412         dbg(1, "LinearDataModel::dump(%s)\n", dd.root.c_str());
00413 
00414         for (int ch = 1; ch <= acq_type.getNumChannels(); ++ch){
00415                 char buf[16];
00416                 sprintf(buf, "%sCH%02d", AcqDataModel::pfx.c_str(), ch);
00417                 File f(dd.root, buf);
00418                 vector<T> &data = *cooked[ch];
00419 
00420                 T* pd = &data[0];
00421                 dbg(1+(ch!=1), "ch:%02d size:%d %08x %08x %08x %08x",
00422                     ch, data.size(), pd[0], pd[1], pd[2], pd[3]);
00423 
00424                 fwrite(&data[0], sizeof(T), data.size(), f.getFp());
00425         }
00426 
00427         if (eventSignatures.size() > 0){
00428                 dumpTimebase(dd);
00429         }
00430 }
00431 
00432 AcqDataModel* AcqDataModel::create(
00433                 const AcqType& acq_type,
00434                 string _scanlist, string _channel_mask)
00435 {
00436         dbg(1, "model :%s", acq_type.model.c_str());
00437 
00438         if (acq_type.model.find("acq132") != string::npos){
00439                 return new Acq132DataModelAdapter(
00440                         acq_type, _scanlist, _channel_mask);
00441         }else{
00442                 switch(acq_type.getWordSize()){
00443                 case 2:
00444                         return new LinearDataModel<short>(
00445                                 acq_type, _scanlist, _channel_mask);
00446                 case 4:
00447                         return new LinearDataModel<int>(
00448                                 acq_type, _scanlist, _channel_mask);
00449                 default:
00450                         err("TODO: non-integer data size");
00451                         return 0;
00452                 }
00453         }
00454 }
00455 
00456 AcqDataModel::AcqDataModel(
00457                 const AcqType& _acq_type, 
00458                 string _scanlist, string _channelMask) :
00459                 acq_type(_acq_type),
00460                 scanlist(_scanlist),
00461                 channelMask(_channelMask),
00462                 has_timebase(true),
00463                 ch_name_core("CH")
00464 
00465 {
00466         acq_cal = AcqCal::create(acq_type);
00467 }
00468 
00469 void AcqDataModel::setAcqCal(AcqCal* _acq_cal){
00470         if (acq_cal){
00471                 AcqCal::destroy(acq_cal);
00472         }
00473         acq_cal = _acq_cal;
00474 }
00475 
00476 void AcqDataModel::dump(string root){
00477         DumpDef dd(root);
00478         dump(dd);
00479 }
00480 
00481 enum WCP AcqDataModel::wallclock_policy;