migrate to standard svn repo layout

This commit is contained in:
Markus Rosenstihl 2014-06-26 11:10:51 +00:00
commit 5545917824
168 changed files with 25429 additions and 0 deletions

24
Makefile Normal file
View File

@ -0,0 +1,24 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
.PHONY: clean install all
SUBDIRS=core drivers machines tests doc tools
PREFIX=$(shell cd && pwd)/nmr_software
all:
for d in $(SUBDIRS); do \
$(MAKE) PREFIX=$(PREFIX) -C $$d all || exit 1; \
done
clean:
rm -f *~ && for d in $(SUBDIRS); do $(MAKE) -C $$d clean; done
install:
install -d $(PREFIX); \
for d in $(SUBDIRS); do $(MAKE) PREFIX=$(PREFIX) -C $$d install; done

55
core/Makefile Normal file
View File

@ -0,0 +1,55 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
CXX=g++
CXXFLAGS=-g -O0 -Wshadow -Wall -Wextra -pedantic
CXXCPPFLAGS= -I. -I.. -I/usr/include `pkg-config --cflags glib-2.0`
LIBS=-lexpat -L/usr/lib `pkg-config --libs glib-2.0`
AR=ar
.PHONY: clean all install
CORE_CLASSES=job.o job_receiver.o result.o core_config.o core.o states.o xml_states.o xml_result.o backend_config_reader.o
all: core.a
../tools/add_endline.exe: ../tools/add_endline.cpp
$(CXX) $< -o $@
core.a: $(CORE_CLASSES)
$(AR) rs $@ $^
backend_config_reader.o: backend_config_reader.cpp backend_config_reader.h
core.o: core.cpp core.h core_exception.h job.h
result.o: result.cpp result.h
job.o: job.cpp job.h
job_receiver.o: job_receiver.cpp job_receiver.h job.h
core_config.o: core_config.cpp core_config.h core_exception.h
states.o: states.cpp states.h
xml_states.o: xml_states.cpp xml_states.h states.h
xml_result.o: xml_result.cpp xml_result.h result.h
clean: ../tools/add_endline.exe
for f in *.cpp *.h; do ../tools/add_endline.exe $$f; done; \
rm -f *.o *~ core.a
DLLS=/bin/cygexpat-0.dll /bin/cygwin1.dll
install:
for dll in $(DLLS);do test -f $$dll && install $$dll $(PREFIX); done
.cpp.o:
@echo " Compiling $<"
@$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) $< -o $@

View File

@ -0,0 +1,91 @@
/* **************************************************************************
Author: Stefan Reutter
Created: August 2013
****************************************************************************/
#include <cstring>
#include "backend_config_reader.h"
namespace DAMARIS
{
BackendConfigReader::BackendConfigReader(const gchar *configPath):
_damarisConfigPath(configPath),
_keyFile (NULL)
{
loadConfig();
// if loadConfig succeeded, we should have a valid config file
}
void BackendConfigReader::loadConfig()
{
bool fileFound = false;
// check if user config file exists
const gchar* usr_config_dir = g_get_user_config_dir();
int configPathLength = strlen(usr_config_dir) + strlen(_damarisConfigPath);
gchar *cfgFileName = new gchar[configPathLength > 1024 ? configPathLength: 1024];
strcpy(cfgFileName, usr_config_dir);
strcat(cfgFileName, _damarisConfigPath);
_keyFile=g_key_file_new ();
GError *error=NULL;
if (!loadConfigFile(cfgFileName, G_KEY_FILE_NONE, error)) {
// if the user config doesn't exist, attempt to read from a system config
const gchar *const *sys_config_dirs = g_get_system_config_dirs();
unsigned int i = 0;
while (true)
{
if (sys_config_dirs[i]==NULL) { break;} // no more paths to check
// avoid buffer overrun
configPathLength = strlen(sys_config_dirs[i]) + strlen(_damarisConfigPath);
if (configPathLength > 1024)
{
gchar *temp = cfgFileName;
cfgFileName = new gchar[configPathLength];
delete[] temp;
}
strcpy(cfgFileName, sys_config_dirs[i]);
strcat(cfgFileName, _damarisConfigPath);
if (loadConfigFile(cfgFileName, G_KEY_FILE_NONE, error))
{
// found a config file, breaking
fileFound = true;
break;
}
i++;
}
} else
{
fileFound = true;
}
delete[] cfgFileName;
if (!fileFound)
{
throw(BackendConfigException("No config file found\n"));
}
}
bool BackendConfigReader::loadConfigFile(const gchar *keyFileName, GKeyFileFlags flags, GError *error)
{
if (!g_key_file_load_from_file (_keyFile, keyFileName, flags, &error)) {
if (error->code == G_FILE_ERROR_NOENT) // file not found
{
return false;
} else
{
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "%s", error->message);
}
error = NULL;
}
return true;
}
}// namespace DAMARIS

View File

@ -0,0 +1,119 @@
/* **************************************************************************
Author: Stefan Reutter
Created: August 2013
****************************************************************************/
#ifndef DAMARIS_backend_config_reader_h
#define DAMARIS_backend_config_reader_h
#include <glib.h>
#include <stdexcept>
namespace DAMARIS
{
/**
* \defgroup BackendConfigReader Backend Config Reader
* \ingroup machines
*
* Allows reading in a backend configuration file implemented as a Glib Key-Value file
*/
/*@{*/
/**
* Exception class for the config file.
*/
class BackendConfigException : public std::runtime_error
{
public:
explicit BackendConfigException(const std::string& msg) throw() : std::runtime_error(msg) {};
explicit BackendConfigException(const char* msg) throw() : std::runtime_error(msg) {};
virtual ~BackendConfigException() throw() {};
};
/**
* Class for reading a backend configuration file. Must be constructed with a c string providing the relative path to the config file.
*
* BackendConfigReader will first look in the XDG user config folder for the file, then in all system config folders. If no
* config file is found in any of these locations, a BackendConfigException will be thrown.
*
* Example:
* \code
* DAMARIS::BackendConfigReader cfgReader("/damaris/backend.conf");
* std::cout << cfgReader.getInteger("ADC", "id") << "\n";
* \endcode
*
*/
class BackendConfigReader
{
public:
/**
* \param configPath C String containing the relative path to the config file
*/
BackendConfigReader(const gchar *configPath);
~BackendConfigReader() { g_key_file_free(_keyFile); }; // I hope this is exception safe
/* -------------------------------------------------------------------- */
/* The following functions are encapsulating the underlying GKeyFile functionality */
inline gboolean getBoolean(const gchar *groupName, const gchar *key) {GError* err = NULL; bool ret = g_key_file_get_boolean(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
inline gdouble getDouble(const gchar *groupName, const gchar *key) {GError* err = NULL; double ret = g_key_file_get_double(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
inline gint getInteger(const gchar *groupName, const gchar *key) {GError* err = NULL; int ret = g_key_file_get_integer(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
inline gchar* getValue(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_value(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
inline gchar* getString(const gchar *groupName, const gchar *key) {GError* err = NULL; gchar* ret = g_key_file_get_string(_keyFile, groupName, key, &err); if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);} return ret;};
/**
* This allows you to pass a function pointer to BackendConfigReader in order to call one of the functions on GKeyFile that are not driectly provided.
*
* Because a function pointer needs to know the full signature, the function pointer must reflect this.
*
* Example usage:
* \code
* gchar* groupName; // initialized
* gchar* key; // initialized
* gint64 (*f)(GKeyFile*, const gchar *, const gchar *, GError*) = &g_key_file_get_int64;
* gint64 value = getSomething<gint64> (f, groupName, key);
* \endcode
*/
template<typename returnType>
returnType getSomething(returnType (*func)(GKeyFile*, const gchar *, const gchar *, GError*), const gchar *groupName, const gchar *key)
{
GError* err = NULL;
returnType ret = func(_keyFile, groupName, key, &err);
if (err) {g_error("GLib Error code %i, message: %s", err->code, err->message);}
return ret;
}
/* -------------------------------------------------------------------- */
private:
/**
* Attempt to load the defined relative config path.
*
* First, attempt to load the file from the user config dir. If that fails, attempt to load from the system config dir. If that fails as well, exit the program.
*/
void loadConfig();
/**
* Load a specific config file. Returns true on success, false on file not found. Throws an error otherwise.
*/
bool loadConfigFile(const gchar *keyFileName, GKeyFileFlags flags, GError *error);
/**
* (Relative) Path to the damaris backend config
*/
const gchar *_damarisConfigPath;
GKeyFile *_keyFile;
};
/*@}*/
} // namespace DAMARIS
#endif // include guard

50
core/constants.h Normal file
View File

@ -0,0 +1,50 @@
/* constants.h
* Author: Stefan Reutter (2011)
*/
#ifndef CONSTANTS_H
#define CONSTANTS_H
/* Settings for the spectrum M2i ADC driver */
/** default impedance for the spectrum ADC in Ohm */
const double ADC_M2I_DEFAULT_IMPEDANCE = 1e6;
/** 50 Ohm impedance is allowed as well */
const double ADC_M2I_ALLOWED_IMPEDANCE = 50.0;
/** default offset */
const int ADC_M2I_DEFAULT_OFFSET = 0;
/** default channel bitmask (2 first channels enabled) */
const int ADC_M2I_DEFAULT_CHANNELS = 3;
/** default sensitivity in volt */
const double ADC_M2I_DEFAULT_SENSITIVITY = 10.0;
/** default resolution in bits per sample */
const int ADC_M2I_DEFAULT_RESOLUTION = 14;
/** allowed sensitivity settings */
const double ADC_M2I_ALLOWED_SENSITIVITY[6] = {0.2, 0.5, 1, 2, 5, 10};
const int ADC_M2I_ALLOWED_SENSITIVITY_LENGTH = 6;
/** size of the pre trigger, must be at least 4 and increase in steps of 4 */
const int ADC_M2I_PRETRIGGER = 4;
/** size of the post trigger, must be at least 4 and increase in steps of 4 */
const int ADC_M2I_POSTTRIGGER = 4;
/* Settings for the spectrum MI ADC driver */
/** default impedance for the spectrum ADC in Ohm */
const double ADC_MI_DEFAULT_IMPEDANCE = 1e6;
/** 50 Ohm impedance is allowed as well */
const double ADC_MI_ALLOWED_IMPEDANCE = 50.0;
/** default offset */
const int ADC_MI_DEFAULT_OFFSET = 0;
/** default channel bitmask (2 first channels enabled) */
const int ADC_MI_DEFAULT_CHANNELS = 3;
/** default sensitivity in volt */
const double ADC_MI_DEFAULT_SENSITIVITY = 10.0;
/** default resolution in bits per sample */
const int ADC_MI_DEFAULT_RESOLUTION = 14;
/** allowed sensitivity settings */
const double ADC_MI_ALLOWED_SENSITIVITY[6] = {0.2, 0.5, 1, 2, 5, 10};
const int ADC_MI_ALLOWED_SENSITIVITY_LENGTH = 6;
#endif /* CONSTANTS_H_ */

303
core/core.cpp Normal file
View File

@ -0,0 +1,303 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <math.h>
#include <errno.h>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLException.hpp>
#include "core.h"
#include "job_receiver.h"
#include "xml_result.h"
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
volatile int core::triggered_restart=0;
volatile int core::quit_signal=0;
volatile int core::term_signal=0;
void restart_signal_handler(int signal) {
if (signal==SIGUSR1)
core::triggered_restart=1;
return;
}
void quit_signal_handler(int signal) {
if (signal==SIGQUIT)
core::quit_signal=1;
return;
}
void term_signal_handler(int signal) {
if (signal==SIGINT || signal==SIGTERM) {
core::term_signal=1;
}
return;
}
core::core(const core_config& configuration) {
// time of start
if (-1==time(&start_time))
{
throw core_exception("could not determine start time");
}
// store away cwd for later reset
_cwd = getcwd(NULL, 0);
if (_cwd == NULL)
{
throw core_exception("Getting the current working directory failed");
}
// cd to spool directory if necessary
if (!configuration.spool_directory.empty()) {
// change to given directory
if (0!=chdir(configuration.spool_directory.c_str())) {
throw core_exception(std::string("can not change to \"")+configuration.spool_directory+"\"");
}
}
char* spoolDir = getcwd(NULL, 0);
the_configuration.spool_directory=spoolDir;
free(spoolDir);
// allocate job parser
job_parser=new job_receiver("");
// reset counter
job_counter=0;
if (configuration.job_filename_pattern.empty())
the_configuration.job_filename_pattern="job.%09lu";
else
the_configuration.job_filename_pattern=configuration.job_filename_pattern;
if (configuration.result_filename_pattern.empty())
the_configuration.result_filename_pattern=the_configuration.job_filename_pattern+".result";
else
the_configuration.result_filename_pattern=configuration.result_filename_pattern;
if (configuration.job_poll_wait<0.001)
the_configuration.job_poll_wait=0.1;
else
the_configuration.job_poll_wait=configuration.job_poll_wait;
// todo valid value?!
the_configuration.result_encoding=configuration.result_encoding;
// init hardware is done by derived class
the_hardware=NULL;
// install signal handler to reset the job counter
signal(SIGUSR1,restart_signal_handler);
signal(SIGQUIT,quit_signal_handler);
signal(SIGTERM,term_signal_handler);
signal(SIGINT,term_signal_handler);
quit_mainloop=0;
}
int core::write_state() const {
if (!core_name().empty())
return core_state(*this).dump_state(core_name()+".state");
else
return core_state(*this).dump_state("core.state");
}
int core::remove_state() const {
std::string state_filename;
if (core_name().empty())
state_filename="core.state";
else
state_filename=core_name()+".state";
if (access(state_filename.c_str(),F_OK)!=0)
throw core_exception("the core state file vanished...");
if (0!=remove(state_filename.c_str()))
throw core_exception(std::string("could not remove the core state file \"")+state_filename+"\"");
return 0;
}
int core::run() {
// writes the config and process id to file
write_state();
quit_mainloop=0;
while (!quit_mainloop && term_signal==0 && quit_signal==0) {
// wait for a job to run...
job * this_job=NULL;
result* this_result=NULL;
try {
this_job=wait_for_input();
}
catch (const job_exception& je) {
this_result=new error_result(job_counter,je);
}
if (quit_mainloop!=0 || term_signal!=0) {
if (this_job!=NULL) delete this_job;
break;
}
// do the work ...
if (this_job!=NULL) {
this_result=do_the_job(*this_job);
if (term_signal!=0) {
delete this_job;
if (this_result!=NULL) delete this_result;
break;
}
}
else
this_job=new job(job_counter);
if (this_result==NULL) {
this_result=new error_result(job_counter,"unexpected: did not get any result");
}
// tell them...
write_to_output(*this_job,*this_result);
delete this_result;
delete this_job;
job_counter++;
}
// deletes config file
remove_state();
return 0;
}
job* core::wait_for_input() {
if (triggered_restart!=0) {
job_counter=0;
triggered_restart=0;
}
if (quit_signal!=0 || term_signal!=0) {
quit_mainloop=1;
return (job*)NULL;
}
std::string job_filename;
struct stat job_stat;
// polling loop
while(1) {
if (job_filename.empty()) {
char* job_filename_buffer=(char*)malloc(the_configuration.job_filename_pattern.size()+100);
if (job_filename_buffer==NULL)
throw core_exception("could not allocate memory for job filename");
snprintf(job_filename_buffer,
the_configuration.job_filename_pattern.size()+100,
the_configuration.job_filename_pattern.c_str(),
job_counter);
job_filename=job_filename_buffer;
free(job_filename_buffer);
}
int stat_success=stat(job_filename.c_str(), &job_stat);
if (0==stat_success && job_stat.st_size>0) {
int file_access=access(job_filename.c_str(),F_OK);
if (0==file_access) {
// job_reciever creates a job
job* new_job=job_parser->receive(job_filename);
if (new_job->no()!=job_counter) {
new_job->job_no=job_counter;
fprintf(stderr,"%s : corrected job number\n", job_filename.c_str());
}
return new_job;
}
}
usleep((size_t)floor(the_configuration.job_poll_wait*1.0e6));
// respect triggered restart of program
if (quit_signal!=0 || term_signal!=0) {
quit_mainloop=1;
break;
}
if (triggered_restart!=0) {
job_counter=0;
job_filename="";
triggered_restart=0;
}
}
return (job*)NULL;
}
result* core::do_the_job(job& the_job) {
// look for main loop commands, like quit, wait or nop
control* cjob=dynamic_cast<control*>(&the_job);
if (cjob!=NULL) {
return cjob->do_it(this);
}
// experiments
experiment* ejob=dynamic_cast<experiment*>(&the_job);
if (ejob!=NULL) {
result* r=ejob->do_it(the_hardware);
return r;
}
// experiments
configuration* confjob=dynamic_cast<configuration*>(&the_job);
if (confjob!=NULL) {
result* r=confjob->do_it(the_hardware);
return r;
}
// execute everything else without parameter ...
return NULL; //the_job.do_it();
}
int core::write_to_output(const job& job_done, const result& result_gained){
if (job_done.no()!=result_gained.no()) throw core_exception(std::string(__FUNCTION__)+": job and result number do not match");
// create job file name
char* expansion_buffer=(char*)malloc(the_configuration.result_filename_pattern.size()+100);
if (expansion_buffer==NULL)
throw core_exception(std::string(__FUNCTION__)+": could not allocate expansion_buffer");
snprintf(expansion_buffer,the_configuration.result_filename_pattern.size()+100,the_configuration.result_filename_pattern.c_str(),job_done.no());
std::string resultfile=expansion_buffer;
free(expansion_buffer);
// remove old result file
if (unlink(resultfile.c_str())==-1 && errno!=ENOENT) {
throw core_exception(std::string(__FUNCTION__)+": could not remove existing old result file");
}
if (term_signal!=0) return 0;
// write to temporary file
std::string tempfile=resultfile+".temp";
if (unlink(tempfile.c_str())==-1 && errno!=ENOENT) {
throw core_exception(std::string(__FUNCTION__)+": could not remove existing temporary result file");
}
if (term_signal!=0) return 0;
int write_result=xml_result_writer(the_configuration.result_encoding).write_to_file(tempfile,&result_gained);
if (term_signal!=0) {
unlink(tempfile.c_str());
return 0;
}
// finally move temporary file to result file
if (0!=rename(tempfile.c_str(),resultfile.c_str()))
throw core_exception(std::string(__FUNCTION__)+": could not rename result-tempfile");
return write_result;
}
core::~core() {
// todo: send termination message
// release hardware
// reset cwd
chdir(_cwd);
if (_cwd != NULL) free(_cwd);
if (the_hardware!=NULL) delete the_hardware;
if (job_parser!=NULL) delete job_parser;
}

176
core/core.h Normal file
View File

@ -0,0 +1,176 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef CORE_H
#define CORE_H
#include "core/job.h"
#include "core/result.h"
#include "core/job_receiver.h"
#include "core/core_exception.h"
#include "core/core_config.h"
#include "machines/hardware.h"
#include <string>
#include <time.h>
/**
\mainpage
\section sec_introduction Introduction to DAMARIS backends
DAMARIS stands for DArmstadt MAgnetic Resonance Instrument Software. There is a homepage avaialbe at http://www.fkp.physik.tu-darmstadt.de/damaris/
The project started as study for a common software base for different spectrometer hardware setups.
The first time of this project was determined by
- the need of software for SpinCore and TiePie Hardware,
- and the wish for a "better" and unified software for our spectrometers.
Next step is a functional raw spectrometer control for the mobile NMR spectrometer. Main contributors are Achim and Holger.
\section sec_guide_doc Guide to the documentation
- For the user interface programers the modules about \link job jobs\endlink, \link result results\endlink
and their \link xmlstateinterface xml representation \endlink are useful.
- For the spectrometer maintainer the hardware interface and drivers section is recommended. Each spectrometer ends up
in \link machines machine specification\endlink. This program contains the <b>main</b> procedure.
- The system programer and the people who would like to see how everything is linked together, should visit the core class
implementation and the \link design design considerations\endlink.
\section sec_source_code Source Code
The source code of this project is available from CVS at svn://element.fkp.physik.tu-darmstadt.de/share/SoftwareDevelopment/svnrepository/damaris and also accessible via a web frontend http://element.fkp.physik.tu-darmstadt.de/cgi-bin/viewcvs.cgi/damaris/
*/
/**
\page design General Design Considerations
The central ability of a spectormeter driver is to preform a single shot experiment. The selection of the
pulse sequence, the repitition and also the graphical monitoring is beyond the concept of a driver.
So the frontend-backend approach should be suitable. It is base of the picture:
\image html software_design_overview_scaled.png
To the left in grey some spectormeters are depicted. Each machine-driver can control one. This machine-driver shown to
the left in blue is called core. It recieves jobs in form of a file, processes the necessary informations and performs
an experiment. After receiving the data from an analog digital converter and waiting for the pulse driver to quit, the
next experiment is taken from a queue.
On the right side some possible sources for these jobs are listed. This is the users side. In contrast to other
universal spectrometer control software, it is not intended to have only one program, that does everything.
One can identify different tasks in NMR programs:
- The hardware drivers perform the experiment.
- The monitor functions provide a direct view on the data, to check, if the instrument is running properly.
- The data reduction unit processes data to get the essential measurement data.
- The evaluation routines derive the relevant physical quantity
- The measurement schedule tells the spectrometer, what to do next
The programs, except the hardware driver, control the experiment without dealing with the details of spectrometer setup.
\todo Gating and dead time should be set by driver. Sofar the experiment file must contain these informations.
The users side should be dominated by the users focus, monitoring single shots or scheduling long measurment sequences.
*/
/** works on jobs and gains results or errors */
class core {
public:
/** 1: if a fatal error occurs
or just a quit command
0: go on
*/
int quit_mainloop;
/**
*/
static volatile int triggered_restart;
/**
*/
static volatile int quit_signal;
/**
*/
static volatile int term_signal;
/**
the path configuration....
*/
core_config the_configuration;
/**
the time of instantiation
*/
time_t start_time;
/**
counts the recieved jobs
counting starts from 0
*/
size_t job_counter;
/**
keeps the parser on standby
*/
job_receiver* job_parser;
/**
polls for the job files
*/
job* wait_for_input();
/**
do the experiment or some other things....
*/
result* do_the_job(job& the_job);
/**
tell the result by writing to another file
\exception core_exception if job no does not fit to result no
*/
int write_to_output(const job& job_done, const result& result_gained);
/**
*/
hardware* the_hardware;
public:
/**
setup the hardware and control structure for experiments
\param configuration an initial configuration, e.g. derived by parameter parsing
*/
core(const core_config& configuration);
/**
writes the actual configuration to file when mainloop is started
*/
virtual int write_state() const;
/**
*/
virtual int remove_state() const;
/**
execute incomming instructions
*/
virtual int run();
/**
return the cores name
*/
virtual const std::string& core_name() const=0;
/**
switch off hardware, end core
*/
virtual ~core();
char* _cwd;
};
#endif

179
core/core_config.cpp Normal file
View File

@ -0,0 +1,179 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "core_config.h"
#include "core_exception.h"
#include "xml_result.h"
#include "core.h"
#include <cerrno>
#include <unistd.h>
#include <expat.h>
#include <math.h>
core_config::core_config(const char** argv, int argc) {
spool_directory=".";
result_filename_pattern="job.%09lu.result";
job_filename_pattern="job.%09lu";
job_poll_wait=0.1;
result_encoding=xml_result_writer::base64;
// find spool directory argument
for (int i=1; i<argc; ++i) {
if (strncmp(argv[i],"--spool",7)==0) {
if (argv[i][7]==0) {
if (i>=argc || argv[i+1]==NULL) {
throw core_exception("spool directory not specified after --spool argument");
}
++i;
spool_directory=argv[i];
} else if (argv[i][7]=='=') {
spool_directory=(argv[i]+8);
} else {
throw core_exception("spool directory not specified after --spool argument");
}
} else {
fprintf(stderr, "ignoring argument %s\n", argv[i]);
}
}
}
int core_config::xml_write_core_config_lines(FILE* f) const {
fprintf(f,"<jobs spool=\"%s\" pattern=\"%s\" polltime=\"%g\"/>\n", spool_directory.c_str(), job_filename_pattern.c_str(), job_poll_wait);
std::string encoding_name;
switch (result_encoding) {
case xml_result_writer::defaultmode:
encoding_name="default"; break;
case xml_result_writer::separate_file:
encoding_name="separate_file"; break;
case xml_result_writer::base64:
encoding_name="base64"; break;
case xml_result_writer::ascii:
encoding_name="ascii"; break;
case xml_result_writer::csv:
encoding_name="csv"; break;
case xml_result_writer::hex:
encoding_name="hex"; break;
default:
encoding_name="unknown";
}
fprintf(f,"<results spool=\"%s\" pattern=\"%s\" encoding=\"%s\"/>\n", spool_directory.c_str(), result_filename_pattern.c_str(),encoding_name.c_str());
return 0;
}
int core_config::xml_read_core_config_startElement(const std::string& name, const xml_attrs& attrs) {
if (name=="jobs") {
xml_attrs::const_iterator spool_attr=attrs.find("spool");
xml_attrs::const_iterator pattern_attr=attrs.find("pattern");
xml_attrs::const_iterator polltime_attr=attrs.find("polltime");
if (spool_attr!=attrs.end()) spool_directory=spool_attr->second;
if (pattern_attr!=attrs.end()) job_filename_pattern=pattern_attr->second;
char* polltime_char;
job_poll_wait=(size_t)floor(1e6*strtod(polltime_attr->second.c_str(),&polltime_char));
if (polltime_char==polltime_attr->second.c_str())
throw core_exception("polltime is no valid number");
}
else if (name=="results") {
xml_attrs::const_iterator spool_attr=attrs.find("spool");
xml_attrs::const_iterator pattern_attr=attrs.find("pattern");
if (spool_attr!=attrs.end()) spool_directory=spool_attr->second;
if (pattern_attr!=attrs.end()) result_filename_pattern=pattern_attr->second;
/* todo: result encoding */
}
return 0;
}
void xml_core_status_startelement_handler(core_state* cs, const char* name, char* const* attrs) {
size_t i;
xml_attrs the_attrs;
for (i=0;attrs[i]!=0;i+=2) {
the_attrs[attrs[i]]=attrs[i+1];
}
cs->xml_read_core_state_startElement(std::string(name),the_attrs);
}
int core_state::xml_read_core_state_startElement(const std::string& name, const xml_attrs& attrs) {
if (name=="state") {
xml_attrs::const_iterator pid_attr=attrs.find("pid");
if (pid_attr==attrs.end()) throw core_exception("no pid attribute found");
char* pid_char;
pid=strtol(pid_attr->second.c_str(),&pid_char,10);
if (pid_char==pid_attr->second.c_str()) throw core_exception("pid attribute did not contain a number");
xml_attrs::const_iterator name_attr=attrs.find("name");
if (name_attr!=attrs.end()) core_name=name_attr->second;
}
else
xml_read_core_config_startElement(name,attrs);
return 0;
}
int core_state::dump_state(const std::string& statefilename) const {
// first delete old state file
int unlink_result=unlink(statefilename.c_str());
if (unlink_result!=0 && errno!=ENOENT) {
throw core_exception("could not remove state file "+statefilename+"\"");
}
//then try to write new state file
FILE* configfile=fopen(statefilename.c_str(),"w");
if (configfile==0) throw core_exception("could not write core configuration file \""+statefilename+"\"");
fprintf(configfile,"<?xml version=\"1.0\"?>\n");
fprintf(configfile,"<state name=\"%s\" pid=\"%d\" starttime=\"%ld\">\n", core_name.c_str(), pid, start_time);
xml_write_core_config_lines(configfile);
fprintf(configfile,"</state>\n");
fclose(configfile);
return 0;
}
core_state::core_state(const core& the_core): core_config(the_core.the_configuration) {
pid=getpid();
core_name=the_core.core_name();
start_time=the_core.start_time;
}
core_state::core_state(const std::string& filename): core_config() {
if (access(filename.c_str(),R_OK)!=0)
throw core_exception("could not open status file "+filename);
FILE* configfile=fopen(filename.c_str(),"r");
/* parse document */
XML_Parser p=XML_ParserCreate(NULL);
/* set handler and user data*/
XML_SetStartElementHandler(p,(XML_StartElementHandler)&xml_core_status_startelement_handler);
XML_SetUserData(p,(void*)this);
/* read buffer */
for (;;) {
const size_t BUFF_SIZE=4000;
int bytes_read;
void *buff = XML_GetBuffer(p,BUFF_SIZE);
if (buff == NULL) {
/* handle error */
}
bytes_read = fread(buff, 1, BUFF_SIZE, configfile);
if (bytes_read < 0) {
fclose(configfile);
XML_ParserFree(p);
throw core_exception("error while reading file "+filename);
}
if (! XML_ParseBuffer(p, bytes_read, bytes_read == 0)) {
/* handle parse error */
fclose(configfile);
XML_ParserFree(p);
throw core_exception("error while parsing file "+filename);
}
if (bytes_read == 0)
break;
}
XML_ParserFree(p);
fclose(configfile);
}

78
core/core_config.h Normal file
View File

@ -0,0 +1,78 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef CORE_CONFIG_H
#define CORE_CONFIG_H
#include <cstdio>
#include <string>
#include <map>
#include "core/xml_result.h"
class core;
/**
useful for xml tag parsing
*/
typedef std::map<const std::string, std::string> xml_attrs;
/**
configuration variables influencing the runtime behaviour of core
*/
class core_config {
public:
/**
the directory for job and result data
to be more precise: it is the current workdir of the core where state file goes in
*/
std::string spool_directory;
/**
the pattern to create a result file, should contain %09lu printf token
*/
std::string result_filename_pattern;
/**
name pattern to read jobs, should contain %09lu printf token
*/
std::string job_filename_pattern;
/**
wait time in seconds until next read
*/
double job_poll_wait;
/**
encoding of result files
*/
xml_result_writer::data_save_mode_type result_encoding;
int xml_write_core_config_lines(FILE* f) const;
int xml_read_core_config_startElement(const std::string& name, const xml_attrs& attrs);
core_config():spool_directory("."), result_filename_pattern("job.%09lu.result"), job_filename_pattern("job.%09lu"), job_poll_wait(0.1),result_encoding(xml_result_writer::base64) {}
core_config(const char** argv, int argc);
core_config(const core_config& c): spool_directory(c.spool_directory),
result_filename_pattern(c.result_filename_pattern),
job_filename_pattern(c.job_filename_pattern),
job_poll_wait(c.job_poll_wait),
result_encoding(c.result_encoding)
{}
core_config(const std::string& dir): spool_directory(dir), result_filename_pattern("job.%09lu.result"), job_filename_pattern("job.%09lu"), job_poll_wait(0.1), result_encoding(xml_result_writer::base64) {}
};
class core_state: public core_config {
public:
pid_t pid;
time_t start_time;
std::string core_name;
core_state(const core& the_core);
core_state(const std::string& filename);
int xml_read_core_state_startElement(const std::string& name, const std::map<const std::string, std::string>& attrs);
int dump_state(const std::string& filename) const;
};
#endif

62
core/core_exception.h Normal file
View File

@ -0,0 +1,62 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef CORE_EXCEPTION_H
#define CORE_EXCEPTION_H
#include <stdexcept>
#include <string>
/**
* Common base class for all DAMARIS exceptions. Note that a string error message must be provided upon initialization
*/
class DamarisException: public std::runtime_error
{
public:
explicit DamarisException(const std::string& msg) throw (): std::runtime_error(msg) {}
explicit DamarisException(const char* msg) throw (): std::runtime_error(msg) {}
virtual ~DamarisException() throw () {}
/**
* Override the what() function to automatically allow adding a prefix (simplifies catch statements)
*/
virtual const char* what() const throw ()
{
std::string msg(std::runtime_error::what());
msg.insert(0, prefix());
return msg.c_str();
}
protected:
virtual const std::string prefix() const { return "ERROR (DamarisException): "; }
};
/**
* Core-specific exception
*/
class core_exception: public DamarisException
{
public:
explicit core_exception(const std::string& msg) throw (): DamarisException(msg) {}
explicit core_exception(const char* msg) throw (): DamarisException(msg) {}
virtual ~core_exception() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (core_exception): "; }
};
/**
* Recoverable exception
*/
class RecoverableException: public DamarisException
{
public:
explicit RecoverableException(const std::string& msg) throw (): DamarisException(msg) {}
explicit RecoverableException(const char* msg) throw (): DamarisException(msg) {}
virtual ~RecoverableException() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (RecoverableException): "; }
};
#endif

525
core/job.cpp Normal file
View File

@ -0,0 +1,525 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include <string>
#include <stdarg.h>
#include <cmath>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/dom/DOMWriter.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
#include "core/job.h"
#include "core/core.h"
#include "core/stopwatch.h"
#include "core/xml_states.h"
result* quit_job::do_it(core* c) {
c->quit_mainloop=1;
return new result(job_no);
}
result* pause_job::do_it(core* c) {
/* care of SIGNALS */
pause();
c->triggered_restart=0;
return new result(job_no);
}
result* restart_job::do_it(core* c) {
c->triggered_restart=1;
c->job_counter=0;
return new result(job_no);
}
wait_job::wait_job(const size_t n, const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs): control(n) {
XMLCh* attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("time");
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* length_attr=attrs->getNamedItem(attr_name);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name);
if (length_attr==NULL) {
attr_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("length");
length_attr=attrs->getNamedItem((XMLCh*)"time");
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&attr_name);
if (length_attr==NULL)
throw job_exception("length or time attribute required");
}
char* length_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(length_attr->getNodeValue());
char* parse_end=NULL;
sec=strtod(length_data,&parse_end);
int length_check=strlen(length_data)-(parse_end-length_data);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_data);
if (length_check!=0 || sec<=0)
throw job_exception("wait: length attribute requires number>0");
}
result* wait_job::do_it(core* c) {
struct timespec to_sleep;
to_sleep.tv_sec=(time_t)floor(sec);
to_sleep.tv_nsec=(long int)floor(1e9*(sec-to_sleep.tv_sec));
while (c->term_signal==0) {
struct timespec remaining_time;
int result=nanosleep(&to_sleep,&remaining_time);
if (result==0) break;
to_sleep.tv_sec=remaining_time.tv_sec;
to_sleep.tv_nsec=remaining_time.tv_nsec;
}
/* care of SIGNALS */
if (c->term_signal!=0) return NULL;
return new result(job_no);
}
experiment::experiment(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* exp_data ): job(n){
// create result-document
XMLCh* core_impl_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("core");
XMLCh* doc_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("description");
XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation* impl=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(core_impl_name);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&core_impl_name);
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* description_tags;
description_tags=impl->createDocument();//NULL, doc_name, NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&doc_name);
// get description by name
XMLCh* description_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("description");
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=exp_data->getFirstChild();
int found_description=0;
while (one_child!=NULL) {
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* next_child=one_child->getNextSibling();
if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE &&
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::compareIString(one_child->getNodeName(),description_name)==0) {
// remove this one from list
if (found_description==0) {
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* new_child=description_tags->importNode(one_child, 1);
description_tags->appendChild(new_child);
//XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* old_node=description->replaceChild(new_child, description->getDocumentElement());
//old_node->release();
found_description=1;
}
else {
fprintf(stderr,"experiment: found more than one description section, ignoring\n");
}
exp_data->removeChild(one_child);
one_child->release();
}
one_child=next_child;
}
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&description_name);
if (description_tags->getDocumentElement()!=NULL) {
XMLCh tempStr[100];
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("LS", tempStr, 99);
XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation *impl2=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(tempStr);
XERCES_CPP_NAMESPACE_QUALIFIER DOMWriter *theSerializer=((XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationLS*)impl2)->createDOMWriter();
XERCES_CPP_NAMESPACE_QUALIFIER MemBufFormatTarget mem;
theSerializer->writeNode(&mem,*(description_tags->getDocumentElement()));
theSerializer->release();
description=std::string((char*)mem.getRawBuffer(),mem.getLen());
mem.reset();
}
if (description_tags!=NULL) {description_tags->release();}
// create state tree from rest of dom
one_child=exp_data->getFirstChild();
std::list<state*> found_states;
while (one_child!=NULL) {
if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) {
state_atom* new_one=state_factory(dynamic_cast<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child));
if (new_one!=NULL) {
state* new_state=dynamic_cast<state*>(new_one);
if (new_state!=NULL) {
found_states.push_back(new_state);
}
else {
fprintf(stderr,"experiment: did find something other than a state... forgetting\n");
delete new_one;
}
}
}
one_child=one_child->getNextSibling();
}
// save found states...
if (found_states.empty()) {
experiment_states=NULL;
}
else if (found_states.size()==1) {
experiment_states=found_states.front();
}
else {
state_sequent* new_sequent=new state_sequent;
new_sequent->repeat=1;
for (std::list<state*>::iterator i=found_states.begin();i!=found_states.end();++i) {
new_sequent->push_back(*i);
(**i).parent=new_sequent;
}
experiment_states=new_sequent;
}
}
char* experiment::get_parameter(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element, ...) {
if (element==NULL) return NULL;
va_list names;
va_start(names,element);
const char* attr_name=va_arg(names, const char*);
const XERCES_CPP_NAMESPACE_QUALIFIER DOMAttr* attr=NULL;
while (attr_name!=NULL) {
XMLCh* xml_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(attr_name);
attr=element->getAttributeNode(xml_name);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&xml_name);
if (attr!=NULL) break;
attr_name=va_arg(names, const char*);
}
va_end(names);
if (attr==NULL) return NULL;
return XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(attr->getValue());
}
state_atom* experiment::state_factory(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element) {
if (element==NULL) return NULL;
char* my_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(element->getNodeName());
if(strcasecmp(my_name,"state")==0) {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name);
// read parameters
char* length_string=get_parameter(element,"length","time","t","l",NULL);
if (length_string==NULL) throw job_exception("state requires length");
char* length_string_end;
double length=strtod(length_string,&length_string_end);
int result=strlen(length_string)-(length_string_end-length_string);
// here, we could search for us, s or ms, d or min
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&length_string);
// but now we do not accept anything
if (result!=0) throw job_exception("state requires length as floating point value");
if (length<0) throw job_exception("state's length must be non-negative");
state* new_state=new state(length);
new_state->parent=NULL;
// read states defintions
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild();
while (one_child!=NULL) {
if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) {
state_atom* new_one=state_factory(dynamic_cast<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child));
if (new_one!=NULL) new_state->push_back(new_one);
}
one_child=one_child->getNextSibling();
}
return new_state;
}
else if(strcasecmp(my_name,"sequent")==0) {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name);
// read parameters
size_t repeat=1;
// read parameters
char* repeat_string=get_parameter(element,"repeat","n",NULL);
if (repeat_string!=NULL) {
char* repeat_string_end;
double repeat_d=strtod(repeat_string,&repeat_string_end);
int result=strlen(repeat_string)-(repeat_string_end-repeat_string);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&repeat_string);
// but now we do not accept anything
if (result!=0 || repeat_d<0) throw job_exception("sequent requires loop count as a nonnegative integer value");
repeat=(size_t)floor(repeat_d);
if (1.0*repeat-repeat_d!=0) fprintf(stderr,"rounding non integer towards lower integer");
}
state_sequent* new_sequent=new state_sequent(repeat);
new_sequent->parent=NULL;
// read substates and subsequences
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=element->getFirstChild();
while (one_child!=NULL) {
if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) {
state_atom* new_one=state_factory(dynamic_cast<XERCES_CPP_NAMESPACE_QUALIFIER DOMElement*>(one_child));
if (new_one!=NULL) {
state* new_state=dynamic_cast<state*>(new_one);
if (new_state!=NULL) {
new_state->parent=new_sequent;
new_sequent->push_back(new_one);
}
else {
fprintf(stderr,"experiment: found nonstate in sequent element\n");
delete new_one;
}
}
}
one_child=one_child->getNextSibling();
}
return new_sequent;
}
else if(strcasecmp(my_name,"ttlout")==0) {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name);
// read parameters
ttlout* ttls=new ttlout();
char* id=get_parameter(element,"id","i",NULL);
if (id!=NULL) {
ttls->id=strtol(id,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id);
}
char* value=get_parameter(element,"value",NULL);
if (value!=NULL) {
ttls->ttls=strtoul(value,NULL,0);
// todo: another error message...
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&value);
}
char* channel=get_parameter(element,"channel",NULL);
if (channel!=NULL) {
char* state_value=get_parameter(element,"state",NULL);
if (state_value!=NULL) {
size_t number=strtoul(channel,NULL,0);
char state_char=(state_value)[0];
fprintf(stderr, "been here\n");
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&state_value);
if (state_char=='h' || state_char=='1')
ttls->ttls.set(number,1);
else
ttls->ttls.set(number,0);
}
else {
fprintf(stderr,"found invalid ttl state: ignoring\n");
delete ttls;
ttls=NULL;
}
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel);
}
return (state_atom*)ttls;
}
else if(strcasecmp(my_name,"analogout")==0) {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name);
analogout* aout=new analogout();
// read parameters
char* channel=get_parameter(element,"channel","c","id","i",(char*)NULL);
if (channel!=NULL) {
aout->id=strtoul(channel,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channel);
}
char* frequency=get_parameter(element,"frequency","f",(char*)NULL);
if (frequency!=NULL) {
aout->frequency=strtod(frequency,NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency);
}
char* dac_value=get_parameter(element,"dac_value","d",(char*)NULL);
if (dac_value!=NULL) {
aout->dac_value=strtol(dac_value,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dac_value);
}
char* phase=get_parameter(element,"phase","p",(char*)NULL);
if (phase!=NULL) {
aout->phase=strtod(phase,NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&phase);
}
char* amplitude=get_parameter(element,"amplitude","a",(char*)NULL);
if (amplitude!=NULL) {
aout->amplitude=strtod(amplitude,NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&amplitude);
}
else
aout->amplitude=1.0;
return aout;
}
else if(strcasecmp(my_name,"analogin")==0) {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&my_name);
analogin* ain=new analogin();
// read parameters
char* id=get_parameter(element,"id","i",(char*)NULL);
if (id!=NULL) {
ain->id=strtoul(id,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&id);
}
char* frequency=get_parameter(element,"f","frequency",(char*)NULL);
if (frequency!=NULL) {
ain->sample_frequency=strtod(frequency,NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&frequency);
if (ain->sample_frequency<0) {
delete ain;
throw job_exception("frequency must be non-negative");
}
}
char* samples=get_parameter(element,"s","samples",(char*)NULL);
if (samples!=NULL) {
char* samples_startpos=samples;
while (*samples_startpos!=0 && isspace(*samples_startpos)) ++samples_startpos;
if (*samples_startpos=='-') {
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples);
delete ain;
throw job_exception("number of samples must be non-negative");
}
ain->samples=strtoul(samples,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&samples);
}
char* channels=get_parameter(element,"c","channels",(char*)NULL);
if (channels!=NULL) {
ain->channels = channel_array(strtoul(channels,NULL,0));
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&channels);
ain->nchannels = ain->channels.count();
} else {
ain->channels = channel_array(ADC_M2I_DEFAULT_CHANNELS);
ain->nchannels = ain->channels.count();
}
ain->sensitivity = new double[ain->nchannels]; // initialize sensitivity array
ain->offset = new int[ain->nchannels]; // initialize offset array (offset is in % of sensitivity)
ain->impedance = new double[ain->nchannels]; // initialize impedance array
// get parameters for each channel
for (int i = 0; i < ain->nchannels; i++) {
if (ain->channels[i] == true) { // check if channel bit is true
/* get sensitivity */
char buffer1[100];
char buffer2[100];
sprintf(buffer1, "sen%i", i);
sprintf(buffer2, "sensitivity%i", i);
char* sensitivity = get_parameter(element, buffer1, buffer2,(char*)NULL);
if (sensitivity!=NULL) {
ain->sensitivity[i] = strtod(sensitivity,NULL);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&sensitivity);
} else {
ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; // set to default
}
/* get offset */
sprintf(buffer1, "offset%i", i);
char* offset = get_parameter(element, buffer1, (char*)NULL);
if (offset!=NULL) {
ain->offset[i] = strtol(offset, NULL, 0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&offset);
} else {
ain->offset[i] = ADC_M2I_DEFAULT_OFFSET; // default is 0
}
/* get impedance */
sprintf(buffer1, "impedance%i", i);
char* impedance = get_parameter(element, buffer1, (char*)NULL);
if (impedance!=NULL) {
ain->impedance[i] = strtoul(impedance, NULL, 0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&impedance);
} else {
ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE; // default is 1 MOhm
}
} else { // channel is not enabled, set to defaults
ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY;
ain->offset[i] = ADC_M2I_DEFAULT_OFFSET;
ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE;
}
}
char* resolution=get_parameter(element,"r","res","resolution",(char*)NULL);
if (resolution!=NULL) {
ain->resolution=strtoul(resolution,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&resolution);
}
else
ain->resolution=ADC_M2I_DEFAULT_RESOLUTION; // was 12
return (state_atom*)ain;
}
return NULL;
}
result* experiment::do_it(hardware* hw) {
if (experiment_states==NULL) {
return new error_result(job_no,"did not find any states in job file");
}
fprintf(stderr,"performing experiment job no %" SIZETPRINTFLETTER ", number of exps is %i\n",job_no, static_cast<int>(experiment_states->size()));
result* data=hw->experiment(*experiment_states);
fprintf(stderr,"finished experiment job no %" SIZETPRINTFLETTER "\n\n",job_no);
if (data==NULL)
return new error_result(job_no,"did not get a result from method result* hardware::experiment(const state&) ");
data->description=description;
data->job_no=job_no;
return data;
}
configuration::configuration(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* conf_data ):job(n) {
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* one_child=conf_data->getFirstChild();
while (one_child!=NULL) {
if (one_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::ELEMENT_NODE) {
// found an element
configuration_device_section new_one;
// copy name
char* dev_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(one_child->getNodeName());
new_one.name=dev_name;
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_name);
// copy attributes
XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* dev_attribs=one_child->getAttributes();
if (dev_attribs!=NULL) {
XMLSize_t i=0;
while (1) {
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* dev_attrib=dev_attribs->item(i);
if (dev_attrib==NULL) break;
char* dev_attrib_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(dev_attrib->getNodeName());
char* dev_attrib_value=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(dev_attrib->getNodeValue());
new_one.attributes[dev_attrib_name]=dev_attrib_value;
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_attrib_name);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&dev_attrib_value);
dev_attrib->getNodeValue();
++i;
}
}
// now copy string contents
new_one.data="";
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* child_child=one_child->getFirstChild();
while (child_child!=NULL) {
if (child_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::TEXT_NODE ||
child_child->getNodeType()==XERCES_CPP_NAMESPACE_QUALIFIER DOMNode::CDATA_SECTION_NODE) {
char* text_data=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(child_child->getNodeValue());
new_one.data.append(text_data);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&text_data);
}
child_child=child_child->getNextSibling();
}
configuration_changes.push_back(new_one);
}
one_child=one_child->getNextSibling();
}
}
result* configuration::do_it(hardware* hw) {
result* res=hw->configure(configuration_changes);
configuration_result* cres=dynamic_cast<configuration_result*>(res);
if (cres!=NULL)
cres->job_no=job_no;
else {
configuration_results* cress=dynamic_cast<configuration_results*>(res);
if (cress!=NULL) {
cress->job_no=job_no;
for(configuration_results::iterator i=cress->begin(); i!=cress->end(); ++i)
(*i)->job_no=job_no;
}
}
return res;
}
void configuration_device_section::print(FILE* f) const {
fprintf(f,"name: %s\n",name.c_str());
for (std::map<const std::string, std::string>::const_iterator j=attributes.begin();
j!=attributes.end();
++j) {
fprintf(f," %s: %s\n",j->first.c_str(),j->second.c_str());
} // j
fprintf(f,"data: %s\n", data.c_str());
}
void configuration::print(FILE* f) const {
for (std::list<configuration_device_section>::const_iterator i=configuration_changes.begin();
i!=configuration_changes.end();
++i) {
i->print(f);
} // i
}

218
core/job.h Normal file
View File

@ -0,0 +1,218 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef JOB_H
#define JOB_H
#include <xercesc/dom/DOMNamedNodeMap.hpp>
#include <xercesc/dom/DOMDocument.hpp>
#include <xercesc/dom/DOMElement.hpp>
#include <unistd.h>
#include <string>
#include <sstream>
#include <map>
#include <cstdio>
#include <math.h>
#include "core/result.h"
#include "core/states.h"
#include "core/constants.h"
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
class core;
/**
\defgroup jobs Jobs
\brief jobs to control core and make experiments
the file format for a job file is simple xml: the element name of the root section is also the name of the job.
\verbinclude experiment_job.xml
*/
//@{
/**
\brief exception for job related things
*/
class job_exception: public std::string {
public:
job_exception(const std::string& s): std::string(s){}
};
/**
\brief base class for a job that comes from input
two groups of classe are available:
- core control jobs
- experiment jobs
*/
class job {
public:
size_t job_no;
job(const size_t n): job_no(n) {
}
size_t no() const {
return job_no;
}
virtual int print() const{
printf("job no %" SIZETPRINTFLETTER "\n", job_no);
return 0;
}
virtual ~job() {}
};
/**
base class for core control
*/
class control: public job {
public:
control(size_t n):job(n) {}
virtual result* do_it(core* c)=0;
};
/**
end main loop
*/
class quit_job: public control {
public:
quit_job(const size_t n): control(n) {}
virtual result* do_it(core* c);
};
/**
just do nothing...
*/
class do_nothing_job: public control {
public:
do_nothing_job(const size_t n): control(n) {}
virtual result* do_it(core*) {
/* of course nothing to do...*/
return new result(job_no);
}
};
/**
wait a specified time period
*/
class wait_job: public control {
public:
double sec;
wait_job(const size_t n): control(n) {
sec=0;
}
wait_job(const size_t n, const XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* attrs);
wait_job(const size_t n, double sec_to_wait): control(n) {
sec=sec_to_wait;
}
virtual result* do_it(core* c);
};
/**
infinite pause until a signal comes
*/
class pause_job: public control {
public:
pause_job(const size_t n): control(n) {}
virtual result* do_it(core* c);
};
/**
restarts the cores job queue
*/
class restart_job: public control {
public:
restart_job(const size_t n): control(n) {}
virtual result* do_it(core* c);
};
class hardware;
/**
base class for experiments
*/
class experiment: public job {
private:
state_atom* state_factory(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element);
char* get_parameter(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* element, ...);
public:
/// holds the experiments states
state* experiment_states;
/// contains teh description of this experiment
std::string description;
/// initialise the experiment data
experiment(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* exp_data=NULL );
virtual result* do_it(hardware* hw);
virtual ~experiment() {
if (experiment_states!=NULL) delete experiment_states;
}
};
class configuration_device_section {
public:
std::string name;
std::map<std::string,std::string> attributes;
std::string data;
void print(FILE* f=stdout) const;
};
/**
a configuration job changes the instrument to another state, something like:
* temperature,
* sample position (lateral, axial),
* shim, tuning
these configuration changes generally do not occurr during a pulse sequence and are not very frequent.
*/
class configuration: public job {
public:
/*
suitable data model for each device
*/
std::list<configuration_device_section> configuration_changes;
public:
configuration(size_t n, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* conf_data=NULL );
void print(FILE* f=stdout) const;
result* do_it(hardware* hw);
};
//@}
#endif

178
core/job_receiver.cpp Normal file
View File

@ -0,0 +1,178 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "job_receiver.h"
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMException.hpp>
job_receiver::job_receiver(std::string the_jobfilenamepattern) {
try {
XERCES_CPP_NAMESPACE_QUALIFIER XMLPlatformUtils::Initialize();
}
catch (const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) {
char* ini_error=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage());
job_exception new_exception(std::string("xerces initialisation error: ")+ini_error);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&ini_error);
throw new_exception;
}
jobfilename=NULL;
setFilenamePattern(the_jobfilenamepattern);
parser=new XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser();
if (parser==NULL) {
delete jobfilename;
throw job_exception("could not allocate parser");
}
parser->setValidationScheme(XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser::Val_Always);
parser->setDoNamespaces(true);
errHandler = (XERCES_CPP_NAMESPACE_QUALIFIER ErrorHandler*) new XERCES_CPP_NAMESPACE_QUALIFIER HandlerBase();
parser->setErrorHandler(errHandler);
}
void job_receiver::setFilenamePattern(const std::string& filenamepattern) {
if (jobfilename!=NULL) delete jobfilename;
jobfilenamepattern=filenamepattern;
jobfilenamesize=filenamepattern.size()+20;
jobfilename=new char[jobfilenamesize];
if (jobfilename==NULL) throw job_exception("could not allocate memory for filename");
}
job* job_receiver::receive(size_t no) {
snprintf(jobfilename,jobfilenamesize+20,jobfilenamepattern.c_str(),no);
job* new_job=receive(std::string(jobfilename));
if (new_job->no()!=no) fprintf(stderr, "expected job number %" SIZETPRINTFLETTER " and specified number %" SIZETPRINTFLETTER " are different\n", no, new_job->no() );
new_job->job_no=no;
return new_job;
}
job* job_receiver::receive(const std::string& filename) {
try {
parser->parse(filename.c_str());
}
catch(const XERCES_CPP_NAMESPACE_QUALIFIER XMLException& toCatch) {
char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage());
job_exception je(std::string("XML error: ")+message);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message);
throw je;
}
catch(const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& toCatch) {
char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.msg);
job_exception je(std::string("XML DOM error: ")+message);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message);
throw je;
}
catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXParseException& toCatch) {
// more verbose for parser errors
char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage());
job_exception je(std::string("XML SAX Parser error: ")+message);
char location[100];
snprintf(location,sizeof(location),", line %ld column %ld",toCatch.getLineNumber(),toCatch.getColumnNumber());
je.append(location);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message);
throw je;
}
catch(const XERCES_CPP_NAMESPACE_QUALIFIER SAXException& toCatch) {
char* message = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(toCatch.getMessage());
job_exception je(std::string("XML SAX error: ")+message);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&message);
throw je;
}
// extract root element, root attributes and root name
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc=parser->getDocument();
if (doc==NULL) throw job_exception("xml job document not found");
XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* rootelement=doc->getDocumentElement();
if (rootelement==NULL) throw job_exception("xml job root document not found");
char* docname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(rootelement->getNodeName());
XERCES_CPP_NAMESPACE_QUALIFIER DOMNamedNodeMap* rootattrs=rootelement->getAttributes();
// check the job number
XMLCh* docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no");
XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* jobno_attr=rootattrs->getNamedItem(docnoname);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname);
if (jobno_attr==NULL) {
docnoname=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("no");
jobno_attr=rootattrs->getNamedItem(docnoname);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docnoname);
}
size_t no=0;
if (jobno_attr==NULL) fprintf(stderr,"Warning: job %" SIZETPRINTFLETTER ": root element has no job number\n",no);
else {
char* docno=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(jobno_attr->getNodeValue());
no=strtoul(docno,NULL,0);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docno);
}
job* this_job=NULL;
// determine the job type by name
if (strcasecmp(docname,"quit")==0) {
this_job=new quit_job(no);
}
else if (strcasecmp(docname,"nop")==0) {
this_job=new do_nothing_job(no);
}
else if (strcasecmp(docname,"pause")==0) {
this_job=new pause_job(no);
} /* pause */
else if (strcasecmp(docname,"restart")==0) {
this_job=new restart_job(no);
} /* restart */
else if (strcasecmp(docname,"wait")==0) {
this_job=new wait_job(no,rootattrs);
} /* wait */
else if (strcasecmp(docname,"experiment")==0) {
try {
this_job=new experiment(no, rootelement);
}
catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) {
char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg);
char domerrno[5];
snprintf(domerrno,5,"%d",de.code);
job_exception je("sorry, something happend while parsing experiment job: ");
je.append(domerrmsg);
je.append(", code ");
je.append(domerrno);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg);
// cleanup missing
throw je;
}
}
else if (strcasecmp(docname,"configuration")==0) {
try {
this_job=new configuration(no, rootelement);
}
catch (const XERCES_CPP_NAMESPACE_QUALIFIER DOMException& de) {
char* domerrmsg=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode(de.msg);
char domerrno[5];
snprintf(domerrno,5,"%d",de.code);
job_exception je("sorry, something happend while parsing configuration job: ");
je.append(domerrmsg);
je.append(", code ");
je.append(domerrno);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&domerrmsg);
// cleanup missing
throw je;
}
}
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&docname);
parser->reset();
parser->resetDocument();
parser->resetDocumentPool();
return this_job;
}
job_receiver::~job_receiver() {
delete jobfilename;
delete errHandler;
delete parser;
XERCES_CPP_NAMESPACE_QUALIFIER XMLPlatformUtils::Terminate();
}

53
core/job_receiver.h Normal file
View File

@ -0,0 +1,53 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef JOB_RECEIVER_H
#define JOB_RECEIVER_H
#include <string>
#include "job.h"
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/parsers/XercesDOMParser.hpp>
/**
\ingroup jobs
can create jobs from xml content
*/
class job_receiver {
XERCES_CPP_NAMESPACE_QUALIFIER XercesDOMParser* parser;
XERCES_CPP_NAMESPACE_QUALIFIER ErrorHandler* errHandler;
std::string jobfilenamepattern;
char* jobfilename;
size_t jobfilenamesize;
public:
/**
the receiver should know how to handle the job directory...
*/
job_receiver(const std::string jobfilenamepattern);
void setFilenamePattern(const std::string& filenamepattern);
/**
here, only the number should be given
*/
job* receive(const size_t no);
/**
here, only the filename
*/
job* receive(const std::string& filename);
/**
free everything
*/
~job_receiver();
};
#endif

21
core/result.cpp Normal file
View File

@ -0,0 +1,21 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include <cstdio>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XMLString.hpp>
#include "result.h"
configuration_result::configuration_result(size_t _no): result(_no) {
XMLCh* core_impl_name=XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("core");
XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation* impl=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(core_impl_name);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&core_impl_name);
tag=impl->createDocument();//NULL, doc_name, NULL);
}
configuration_result::~configuration_result() {
tag->release();
}

147
core/result.h Normal file
View File

@ -0,0 +1,147 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef RESULT_H
#define RESULT_H
#include <xercesc/dom/DOM.hpp>
#include <list>
#include <string>
/**
\defgroup results Results of jobs
\brief results are answers to jobs, success, data or errors
Since now only errors, success and adc results are implemented. Also the results are saved in xml.
ToDo: externalise the xml read/write interface
*/
//@{
/** a result that goes to output */
class result {
public:
/** the coresponding job number */
size_t job_no;
/** description, cited from job file */
std::string description;
/** jobs can only be instantiated with the number */
result(size_t _no): job_no(_no) {
}
size_t no() const {
return job_no;
}
virtual ~result(){}
};
/**
simple error message
*/
class error_result: public result {
public:
/** the error message should be human understandable */
std::string error_message;
/**
\brief save error message inside
the errormessage should give a hint, what happened, not only which function found an error
*/
error_result(size_t _no, const std::string& s):result(_no) {
error_message=s;
}
};
/**
\brief stores the data of ADC
*/
class adc_result: public result {
public:
/**
the data: signed 16 bit values of channel 1 and 2 alternating
*/
short int* data;
/**
samples per channel
*/
size_t samples;
/**
sampling frequency for each channel
*/
double sampling_frequency;
/**
number of channels
*/
int nchannels;
/**
instantiation without data is possible
*/
adc_result(size_t _no, size_t s=0, short int* d=NULL, double freq=0, int nchan=2): result(_no) {
data=d;
samples=s;
sampling_frequency=freq;
nchannels = nchan;
}
/**
free the data arrays
*/
virtual ~adc_result() {
if (data!=NULL) free(data);
}
};
/**
*/
class configuration_result: public result {
public:
XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* tag;
configuration_result(size_t _no);
~configuration_result();
};
class configuration_results: public std::list<configuration_result*>, public result {
public:
configuration_results(size_t _no): result(_no) {}
virtual ~configuration_results() {
while (!empty()) {
delete back();
pop_back();
}
}
};
/**
*/
class adc_results: public std::list<adc_result*>, public result {
public:
adc_results(size_t _no): result(_no) {}
virtual ~adc_results() {
while (!empty()) {
delete back();
pop_back();
}
}
};
//@}
#endif

111
core/states.cpp Normal file
View File

@ -0,0 +1,111 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "states.h"
#include "xml_states.h"
state* state_parallel::copy_flat(size_t enroll) const {
return copy_new();
}
state* state_sequent::copy_flat(size_t max_enroll_loop) const {
state_sequent* flat=new state_sequent;
flat->repeat=repeat;
if (flat->repeat==0)
return flat;
// start copying everything...
for (const_iterator i=begin(); i!=end(); ++i) {
const state* this_state=dynamic_cast<const state*>(*i);
// make the state flat, if sequence is no loop
if (this_state!=NULL) {
state* new_flat=this_state->copy_flat(max_enroll_loop);
state_sequent* ss=dynamic_cast<state_sequent*>(new_flat);
// handle the sequence
if (ss!=NULL) {
// skip this part
if (ss->repeat==0) {
delete ss;
continue;
}
// do not use extra sequent section
if (ss->repeat==1) {
while (!ss->empty()) {
flat->push_back(ss->front());
ss->pop_front();
}
delete ss;
continue;
}
flat->push_back(new_flat);
continue;
}
state_parallel* sp=dynamic_cast<state_parallel*>(new_flat);
if (sp!=NULL) {
flat->push_back(new_flat);
continue;
}
// if this is a state, check length...
if (new_flat->length==0.0)
delete new_flat;
else
flat->push_back(new_flat);
continue;
}
// simply append other things
flat->push_back((**i).copy_new());
} // for
// now enrol own loop
if (repeat>1 && repeat<=max_enroll_loop) {
flat->repeat=1;
std::list<state_atom*> orig_list(*flat);
for (size_t loop=1;loop<repeat;++loop)
flat->insert(flat->end(),orig_list.begin(),orig_list.end());
}
return flat;
}
state* state_iterator::get_state() {
if (subsequence_stack.empty()) return NULL; // at the end of traversal
// find a state or a subsequence in actual subsequence
state* next_one=NULL;
for (state_sequent::const_iterator i=subsequence_stack.back().subsequence_pos;
i!=subsequence_stack.back().subsequence->end();
++i) {
next_one=dynamic_cast<state*>(*i);
if (next_one!=NULL) break;
}
// this subsequence is finished, so go on with the upper one
if (next_one==NULL) {
double substate_time=subsequence_stack.back().elapsed_time*subsequence_stack.back().subsequence->repeat;
subsequence_stack.pop_back();
if (subsequence_stack.empty()) {
total_time=substate_time;
return NULL;
}
subsequence_stack.back().elapsed_time+=substate_time;
++subsequence_stack.back().subsequence_pos;
return get_state();
}
// find out, if this is a subsequence
state_sequent* next_sequent=dynamic_cast<state_sequent*>(next_one);
if (next_sequent!=NULL) {
if (next_sequent->repeat!=0) {
subsequence_iterator next_level={next_sequent,next_sequent->begin(),0};
subsequence_stack.push_back(next_level);
}
else {
++(subsequence_stack.back().subsequence_pos);
}
return get_state();
}
// this is a state
return next_one;
}

375
core/states.h Normal file
View File

@ -0,0 +1,375 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef STATES_H
#define STATES_H
#include <list>
#include <bitset>
/** \defgroup states Devices States
\brief different states of devices are defined
Pulse devices are driven by \link state_sequent sequences\endlink of \link state states \endlink,
that can be nested and repeated.
So an appropriate data structure is a tree of sequences, that can contain states or sequences.
Each sequence can be repeated. States have a defined time length and define the state of all
outputs of the pulse device once. So the length of a sequence is the sum of all state durations
and the length of the nested sequences. If the sequence is repeated, the duration has to be
multiplicated with the numbers of loops.
\n
ToDo: State sections that allow parallel sequences inside are an intresting extension of this concept
Inside the \link state states \endlink, the state_atom objects define the state. There are several classes
derived from state_atom .
\n
ToDo: We could define a default state, that is used as template for the states in the sequences.
This definition results in a tree structure, that is traversed <b>"depth-first"</b>. A class for that traversal
is the state_iterator .
*/
//@{
/**
\brief describes the state of one device
the duration of the state is given by the surrounding state section
*/
class state_atom {
public:
/** no time, no repetiton */
virtual state_atom* copy_new() const=0;
virtual ~state_atom() {}
};
//@}
/**
\defgroup derived_stateatom several derived state_atom classes
\ingroup states
*/
//@{
/**
channels of a multichannel device
*/
typedef std::bitset<32> channel_array;
/**
status of ttl lines
*/
class ttlout: public state_atom {
public:
/** some reference to the device */
int id;
/** the ttl levels */
channel_array ttls;
/** default: all off */
ttlout() {
id=0;
ttls=0;
}
ttlout(const ttlout& orig) {
id=orig.id;
ttls=orig.ttls;
}
virtual state_atom* copy_new() const {
return new ttlout(*this);
}
virtual ~ttlout() {}
};
/**
definition of analog output
*/
class analogout: public state_atom {
public:
/** some reference to the device */
int id;
/** amplitude voltage in V, as fallback 0 means off, !=0 means on */
double amplitude;
/** phase in degree */
double phase;
/** frequency in Hz*/
double frequency;
signed dac_value;
/** default: all of */
analogout() {
id=0;
amplitude=0.0;
phase=0.0;
frequency=0.0;
dac_value=0;
}
analogout(const analogout& orig) {
id=orig.id;
amplitude=orig.amplitude;
phase=orig.phase;
frequency=orig.frequency;
dac_value=orig.dac_value;
}
virtual state_atom* copy_new() const {
return new analogout(*this);
}
virtual ~analogout() {}
};
/**
definition of a analog input
*/
class analogin: public state_atom {
public:
/** a reference to the device */
int id;
/** sample frequency */
double sample_frequency;
/** samples each channel */
size_t samples;
/** which channels to record */
channel_array channels;
/** the number of channels */
int nchannels;
/** sensitivity in volts for each channel */
double* sensitivity;
/** impedance in Ohm for each channel */
double* impedance;
/** offset in % of sensitivity for each channel */
int* offset;
/** resolution in bit per sample */
size_t resolution;
/** default: do nothing */
analogin() {
id=0;
sample_frequency=0;
samples=0;
resolution=0;
nchannels = 0;
channels = channel_array(0);
}
analogin(const analogin& orig) {
id=orig.id;
sample_frequency=orig.sample_frequency;
samples=orig.samples;
sensitivity=orig.sensitivity;
resolution=orig.resolution;
offset = orig.offset;
impedance = orig.impedance;
channels = orig.channels;
nchannels = orig.nchannels;
}
virtual state_atom* copy_new() const {
return new analogin(*this);
}
virtual ~analogin() {}
};
//@}
/**
\ingroup states
*/
//@{
class state: public state_atom, public std::list<state_atom*> {
public:
/** \brief how long in seconds this state should be effective */
double length;
/** \brief points to parent state
the root document has NULL, no cyclic references allowed
*/
const state* parent;
/**
initializes an empty state
*/
state(double _length, const state* my_parent=NULL): state_atom(), std::list<state_atom*>(), length(_length), parent(my_parent) {
}
state(const state& orig): state_atom(), std::list<state_atom*>(), length(orig.length), parent(orig.parent) {
for (state::const_iterator i=orig.begin(); i!=orig.end(); i++)
push_back((**i).copy_new());
}
virtual state_atom* copy_new() const {
return new state(*this);
}
virtual state* copy_flat(size_t enroll = 1) const {
(void) enroll; // get rid of compiler unused parameter warning
return new state(*this);
}
virtual ~state() {
while (!empty()) {
if (back()!=NULL) delete (back());
pop_back();
}
}
};
class state_parallel: public state {
public:
/**
possible time alignments
*/
typedef enum {begin_align,end_align,center_align} states_alignment;
/**
define time allignments
*/
states_alignment align;
state_parallel(const state* my_parent=NULL, states_alignment _a=begin_align): state(0.0,my_parent), align(_a) {
}
state_parallel(const state_parallel& orig): state(orig) {
align=orig.align;
}
virtual state* copy_new() const {
return new state_parallel(*this);
}
virtual state* copy_flat(size_t enroll=4) const;
virtual ~state_parallel() {}
};
/**
\brief sequential states with loops
*/
class state_sequent: public state {
public:
/** \brief how often this state or state sequence should be repeated
*/
size_t repeat;
state_sequent(size_t _repeat=1, const state* my_parent=NULL): state(0.0,my_parent), repeat(_repeat) {
}
state_sequent(const state_sequent& orig): state(orig) {
repeat=orig.repeat;
}
virtual state* copy_new() const {
return new state_sequent(*this);
}
virtual state* copy_flat(size_t enroll=4) const;
virtual ~state_sequent() {
while (!empty()) {
if (back()!=NULL) delete (back());
pop_back();
}
}
};
/**
\brief traverses the state tree from state to state
this iterator leaves out the subsequence borders, but returns correct informations about time of the first
traversal of a state and the total count of traversals.
this is a nonconstant iterator, we have to write a const-iterator...
*/
class state_iterator {
public:
typedef struct {
state_sequent* subsequence;
state_sequent::iterator subsequence_pos;
/// time in seconds in a substate until iterator position
double elapsed_time;
} subsequence_iterator;
std::list<subsequence_iterator> subsequence_stack;
/// time in seconds till iterator position for first loop run
double total_time;
/**
start iteration at the beginning of a sequence
*/
state_iterator(state_sequent& subsequence) {
subsequence_iterator the_beginning={&subsequence,subsequence.begin(),0};
subsequence_stack.push_back(the_beginning);
total_time=0.0;
(void)get_state();
}
/**
time of end of first traversal
*/
double get_time() const {
if (subsequence_stack.empty()) return total_time;
double time=0.0;
for (std::list<subsequence_iterator>::const_iterator i=subsequence_stack.begin();
i!=subsequence_stack.end();
++i)
time+=i->elapsed_time;
return time;
}
/**
get count of traversals of this state
*/
size_t get_count() const {
size_t count=1;
for (std::list<subsequence_iterator>::const_iterator i=subsequence_stack.begin();
i!=subsequence_stack.end();
++i)
count*=i->subsequence->repeat;
return count;
}
/**
advance to next state
\return a pointer to next state or NULL if no state was following
*/
state* next_state() {
if (subsequence_stack.empty()) return NULL;
subsequence_stack.back().elapsed_time+=get_state()->length;
++subsequence_stack.back().subsequence_pos;
return get_state();
}
/**
return true, if iterator is at end
*/
int is_last() const {
return subsequence_stack.empty();
}
/**
get a pointer of this state
if the iterator is actually not on a state, it will go to the next (traverse loops and sections)
\return a pointer to the state or NULL, if no state available
*/
state* get_state();
};
/*@}*/
#endif

129
core/stopwatch.h Normal file
View File

@ -0,0 +1,129 @@
#ifndef STOPWATCH_H
#define STOPWATCH_H
#include <sys/time.h>
#include <cstdlib>
#include <cmath>
/**
\brief timing utility
This class can take times, up to microseconds exact
*/
class stopwatch {
/**
time at last start call
*/
timeval starttime;
/**
time at last stop call
*/
timeval stoptime;
/**
to accumulate time intervals, previous interval seconds are saved
*/
long int offset_sec;
/**
to accumulate time intervals, previous interval microseconds are saved
*/
long int offset_usec;
/**
shift elapsed time to offset register and save new start time
*/
void elapsed_save() const {
/* if timer is already stoped, do nothing */
if (timerisset(&starttime)) {
stopwatch* noconst_this=(stopwatch*)this;
gettimeofday(&(noconst_this->stoptime),NULL);
/* elapsed time added to internal pointer */
noconst_this->offset_sec+=(stoptime.tv_sec-starttime.tv_sec);
noconst_this->offset_usec+=(stoptime.tv_usec-starttime.tv_usec);
noconst_this->starttime.tv_sec=stoptime.tv_sec;
noconst_this->starttime.tv_usec=stoptime.tv_usec;
while (offset_usec<0) {
noconst_this->offset_usec+=1000000;
noconst_this->offset_sec-=1;
}
while (offset_usec>=1000000) {
noconst_this->offset_usec-=1000000;
noconst_this->offset_sec+=1;
}
}
}
public:
/**
creates stopwatch, stopped state, 0 time offset
*/
stopwatch() {
timerclear(&starttime);
offset_sec=offset_usec=0;
}
/**
creates stopwatch, stopped state, with time offset from double argument
*/
stopwatch(const double& offset) {
timerclear(&starttime);
offset_sec=(long int)floor(offset);
offset_usec=(long int)(1.0e6*(offset-offset_sec));
}
/**
creates stopwatch, stopped state, with time offset in seconds and microseconds
*/
stopwatch(const long int& _offset_sec, const long int& _offset_usec) {
timerclear(&starttime);
offset_sec=_offset_sec;
offset_usec=_offset_usec;
}
stopwatch(const stopwatch& orig) {
starttime.tv_sec=orig.starttime.tv_sec;
starttime.tv_usec=orig.starttime.tv_usec;
offset_sec=orig.offset_sec;
offset_usec=orig.offset_usec;
}
/**
set timer to zero and start measurement
*/
inline void start() {
gettimeofday(&starttime,NULL);
offset_sec=offset_usec=0;
}
/**
stop or pause time measurement
*/
inline void stop() {
elapsed_save();
/* and clear this pointer */
timerclear(&starttime);
}
/**
continue time measurement
*/
inline void cont() {
if (!timerisset(&starttime)) gettimeofday(&starttime,NULL);
}
inline double elapsed() const {
elapsed_save();
return (1.0e-6*(double)offset_usec)+(double)offset_sec;
}
inline void elapsed(long int& sec, long int& usec) const {
elapsed_save();
sec=offset_sec;
usec=offset_usec;
}
};
#endif /* STOPWATCH_H */

616
core/xml_result.cpp Normal file
View File

@ -0,0 +1,616 @@
#include "xml_result.h"
#include <cstdlib>
#include <xercesc/util/Base64.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/dom/DOMWriter.hpp>
#include <xercesc/framework/MemBufFormatTarget.hpp>
/* ***************************************************************************************************
here starts the reader implementation
*****************************************************************************************************/
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs );
void end_element_stub(void* userdata,const XML_Char* name);
void data_stub(void* userdata, const XML_Char* data, int len);
/**
table for translation base64 -> 0 to 63
*/
static char base64_table[] = {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};
class xml_result_handler {
/**
*/
int alloc_stepsize;
/**
maximum allocated size of buffer list
*/
size_t bufferno_max;
/**
actual or new sample buffer
*/
size_t bufferno;
/**
buffers
*/
short int** buffers;
/**
number of samples (2 values for one sample) in each buffer, if actual buffer, this is the expected number
*/
size_t* sampleno;
/**
*/
double* frequencies;
/**
maximum allocated size of last sample buffer in bytes
*/
size_t bufferlength;
/**
position of in actual sample buffer
*/
size_t bufferpos;
/**
base64 byte buffer
*/
char base64buffer[4];
/**
the length of content in base64buffer
*/
int base64buffer_length;
/**
1 if we are inside a adcdata section, 2 if we are saving a description
*/
int section_state;
/**
error messages, that are found
*/
std::string error_message;
/**
descripton sections
*/
std::string description_text;
public:
xml_result_handler(XML_Parser p) {
/* set event handlers */
alloc_stepsize=1<<10;
XML_SetElementHandler(p,start_element_stub,end_element_stub);
XML_SetCharacterDataHandler(p,data_stub);
XML_SetUserData(p,this);
/* allocate some buffers */
sampleno=(size_t*)malloc(alloc_stepsize*sizeof(size_t));
buffers=(short int**)malloc(alloc_stepsize*sizeof(short int*));
frequencies=(double*)malloc(alloc_stepsize*sizeof(double));
if (sampleno==NULL || buffers==NULL) {
if (sampleno!=NULL) free(sampleno);
if (buffers!=NULL) free(buffers);
if (frequencies!=NULL) free(frequencies);
/* todo throw something */
return;
}
bufferno=0;
buffers[0]=NULL;
bufferno_max=alloc_stepsize;
section_state=0;
}
void start_element(const XML_Char* name, const XML_Char** attrs) {
if (strcmp(name,"adcdata")==0) {
/* is there enough space in array? */
if (bufferno>=bufferno_max) {
bufferno_max+=alloc_stepsize;
buffers=(short int**)realloc((void*)buffers,bufferno_max*sizeof(short int*));
sampleno=(size_t*)realloc((void*)sampleno,bufferno_max*sizeof(size_t));
frequencies=(double*)realloc((void*)frequencies,bufferno_max*sizeof(double));
if (buffers==NULL || sampleno==NULL || frequencies==NULL) fprintf(stderr,"could not reallocate the buffer");
}
/* todo read sample no*/
const XML_Char** pos=attrs;
while (*pos!=NULL) {
if (strcmp(*pos,"samples")==0 || strcmp(*pos,"s")==0) {
sampleno[bufferno]=strtoul(*(pos+1),NULL,0);
}
else if (strcmp(*pos,"frequency")==0 || strcmp(*pos,"f")==0) {
frequencies[bufferno]=strtod(*(pos+1),NULL);
}
pos+=2;
}
if (sampleno[bufferno]!=0) {
bufferlength=sampleno[bufferno]*2*sizeof(short int);
/* fresh buffer */
buffers[bufferno]=(short int*)malloc(bufferlength);
}
else {
bufferlength=0;
buffers[bufferno]=NULL;
}
bufferpos=0;
/* todo check result */
base64buffer_length=0;
section_state=1;
}
if (strcmp(name,"description")) {
section_state=3;
}
if (strcmp(name,"error")==0) {
section_state=2;
}
if (section_state==3) {
description_text+="<";
description_text+=name;
const XML_Char** attr=attrs;
while(*attr!=NULL) {
description_text+=" ";
description_text+=*(attr++);
description_text+="='";
description_text+=*(attr++);
description_text+="'";
}
description_text+=">";
}
}
void end_element(const XML_Char* name) {
if (strcmp(name,"adcdata")==0) {
/* empty base64 buffer */
if (bufferpos+base64buffer_length-1>=bufferlength) {
bufferlength+=alloc_stepsize;
buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength);
/* todo reallocate check */
}
if (base64buffer_length>1) {
unsigned char s=base64buffer[0];
s<<=2;
s|=(base64buffer[1]>>4);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
if (base64buffer_length>2) {
s=base64buffer[1]&15;
s<<=4;
s|=(base64buffer[2]>>2);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
if (base64buffer_length>3) {
s=base64buffer[2]&3;
s<<=6;
s|=base64buffer[3];
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
}
}
}
/* check right sample number */
if (bufferpos!=sampleno[bufferno]*2*sizeof(short int)) {
sampleno[bufferno]=bufferpos/(2*sizeof(short int));
}
/* prepare for next buffer */
bufferlength=0;
bufferno++;
section_state=0;
}
if (strcmp(name,"error")==0) {
if (!error_message.empty() && error_message[error_message.size()-1]!='\n') {
error_message.push_back('\n');
}
section_state=0;
}
if (section_state==3) {
description_text+="<";
description_text+=name;
description_text+="/>";
}
if (strcmp(name,"description")) {
section_state=0;
}
}
void data(const XML_Char* the_data, int len) {
if (section_state==1) {
int pos=0;
while (pos<len) {
/* fill buffer */
while (base64buffer_length<4 && pos<len) {
if (((unsigned char)the_data[pos])<128) {
char value=base64_table[(unsigned char)the_data[pos]];
if (value!=-1) {
base64buffer[base64buffer_length]=value;
base64buffer_length++;
}
}
pos++;
}
/* interprete buffer, that has 4*6 bits=3 byte */
if (base64buffer_length==4) {
/* check the space */
if (bufferpos+3>=bufferlength) {
/* to do reallocate */
bufferlength+=alloc_stepsize;
buffers[bufferno]=(short int*)realloc(buffers[bufferno],bufferlength);
if (buffers[bufferno]==NULL) fprintf(stderr,"could not enlarge the sample buffer\n");
}
/* read three bytes from four letters */
unsigned char s=base64buffer[0];
s<<=2;
s|=(base64buffer[1]>>4);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
s=base64buffer[1]&15;
s<<=4;
s|=(base64buffer[2]>>2);
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
s=base64buffer[2]&3;
s<<=6;
s|=base64buffer[3];
((char*)buffers[bufferno])[bufferpos]=s;
bufferpos++;
base64buffer_length=0;
}
}
}
else if (section_state==3) {
description_text+=the_data;
}
else if (section_state==2) {
error_message+=the_data;
}
}
~xml_result_handler() {
if (sampleno!=NULL && buffers!=NULL) {
size_t pos=0;
while (pos<=bufferno) {
if (buffers[pos]!=NULL) free(buffers[pos]);
pos++;
}
}
if (sampleno!=NULL) free(sampleno);
if (buffers!=NULL) free(buffers);
}
/**
print number of samples and frequency of the obtained data
*/
void print_info(FILE* f) {
if (buffers==NULL || sampleno==NULL) {
fprintf(f,"nothing saved\n");
return;
}
for (size_t i=0; i<bufferno; i++) {
fprintf(f,"%" SIZETPRINTFLETTER ": %" SIZETPRINTFLETTER " samples %f Hz frequency\n",i,sampleno[i],frequencies[i]);
if (buffers[i]!=NULL) {
for (size_t j=0; j<sampleno[i]; j++) {
short int a=buffers[i][j*2];
short int b=buffers[i][j*2+1];
fprintf(f,"%hd %hd\n",a,b);
}
}
}
}
};
void start_element_stub(void* userdata,const XML_Char* name, const XML_Char** attrs ) {
((xml_result_handler*)userdata)->start_element(name,attrs);
}
void end_element_stub(void* userdata,const XML_Char* name) {
((xml_result_handler*)userdata)->end_element(name);
}
void data_stub(void* userdata, const XML_Char* data, int len ) {
((xml_result_handler*)userdata)->data(data,len);
}
xml_result_reader::xml_result_reader() {
jobfilenamepattern="";
}
xml_result_reader::~xml_result_reader() {
}
xml_result_reader::xml_result_reader(const std::string& jobpattern) {
jobfilenamepattern=jobpattern;
}
/* ToDo error handling */
result* xml_result_reader::read(size_t no) {
if (jobfilenamepattern=="") return NULL;
char* tmp_name=(char*)malloc(jobfilenamepattern.size()+50);
if (tmp_name==NULL) return NULL;
snprintf(tmp_name,jobfilenamepattern.size()+50,jobfilenamepattern.c_str(),no);
result* new_result=NULL;
new_result=read(std::string(tmp_name));
free(tmp_name);
return new_result;
}
result* xml_result_reader::read(const std::string& jobfilename) {
/*open file*/
FILE* result_file=fopen(jobfilename.c_str(),"rb");
if (result_file==NULL) return NULL;
XML_Parser parser=XML_ParserCreate(NULL);
if (parser==NULL) {
fclose(result_file);
return NULL;
}
xml_result_handler h(parser);
for (;;) {
const size_t BUFF_SIZE=1<<10;
int bytes_read;
void *buff = XML_GetBuffer(parser, BUFF_SIZE);
if (buff == NULL) {
fprintf(stderr, "could not get the buffer from XML Parser\n");
fclose(result_file);
return NULL;
}
bytes_read = fread(buff, 1, BUFF_SIZE, result_file);
if (bytes_read < 0) {
/* handle error */
fprintf(stderr,"error while reading\n");
break;
}
if (! XML_ParseBuffer(parser, bytes_read, bytes_read == 0)) {
/* handle parse error */
fprintf(stderr,"error while parsing line %d: %s\n",static_cast<int>(XML_GetCurrentLineNumber(parser)),XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
// peaceful end
if (bytes_read == 0) break;
}
fclose(result_file);
return NULL;
}
/* ************************************************************************************************
here starts the writer implementation
**************************************************************************************************/
xml_result_writer::xml_result_writer(data_save_mode_type how) {
resultfilename_pattern="";
data_save_mode=how;
}
xml_result_writer::xml_result_writer(const std::string& pattern, data_save_mode_type how) {
resultfilename_pattern=pattern;
data_save_mode=how;
}
int xml_result_writer::write_to_file(const std::string& filename, const result* res) const {
// find the way to write
const adc_result* adc_res=dynamic_cast<const adc_result*>(res);
if (adc_res!=NULL) {
write_adc_to_file(filename,adc_res);
return 0;
}
const adc_results* adc_ress=dynamic_cast<const adc_results*>(res);
if (adc_ress!=NULL) {
write_adcs_to_file(filename, adc_ress);
return 0;
}
const configuration_results* config_ress=dynamic_cast<const configuration_results*>(res);
if (config_ress!=NULL) {
write_configuration_results_to_file(filename, *config_ress);
return 0;
}
const error_result* err_res=dynamic_cast<const error_result*>(res);
if (err_res!=NULL) write_error_to_file(filename,err_res);
else
write_unknown_to_file(filename, res);
return 0;
}
int xml_result_writer::write_unknown_to_file(const std::string& filename, const result* res) const {
FILE* out=fopen(filename.c_str(),"w");
if (out==0) fprintf(stderr,"could not open file %s\n",filename.c_str());
fprintf(out,"<?xml version=\"1.0\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
if (res==NULL)
fprintf(out,"<!-- got NULL pointer result... -->\n");
else
fprintf(out,"<!-- don't know how to print result... -->\n");
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_error_to_file(const std::string& filename, const error_result* res) const {
/* write an extra message to stderr */
fprintf(stderr,"job %" SIZETPRINTFLETTER ": %s\n",res->job_no,res->error_message.c_str());
FILE* out=fopen(filename.c_str(),"w");
if (out==0) {
fprintf(stderr,"could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
fprintf(out," <error>%s</error>\n",res->error_message.c_str());
fprintf(out,"</result>");
fclose(out);
return 0;
}
int xml_result_writer::write_configuration_results_to_file(const std::string& filename, const configuration_results& ress) const {
FILE* out=fopen(filename.c_str(),"w");
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n<configuration>\n",ress.job_no);
XMLCh tempStr[100];
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::transcode("LS", tempStr, 99);
XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation *impl2=XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationRegistry::getDOMImplementation(tempStr);
XERCES_CPP_NAMESPACE_QUALIFIER DOMWriter *theSerializer=((XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementationLS*)impl2)->createDOMWriter();
for (configuration_results::const_iterator i=ress.begin(); i!=ress.end(); ++i)
if ((*i)->tag->getDocumentElement()!=NULL) {
XERCES_CPP_NAMESPACE_QUALIFIER MemBufFormatTarget mem;
theSerializer->writeNode(&mem,*((*i)->tag->getDocumentElement()));
fwrite((char*)mem.getRawBuffer(),sizeof(char),mem.getLen(),out);
mem.reset();
}
theSerializer->release();
// todo
fprintf(out,"</configuration>\n</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adcs_to_file(const std::string& filename, const adc_results* ress) const {
// fall back to some default mode
data_save_mode_type how=data_save_mode;
if (how==defaultmode) how=ascii;
FILE* out=fopen(filename.c_str(),"w");
if (out==0) {
fprintf(stderr,"could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",ress->job_no);
fwrite(ress->description.c_str(),ress->description.size(),1,out);
fprintf(out,"\n");
int num_res=0;
for (adc_results::const_iterator res=ress->begin(); res!=ress->end(); ++res) {
switch (how) {
case separate_file:
{
// write separate data file
char result_filename[1<<10];
snprintf(result_filename,sizeof(result_filename),"adc.%09" SIZETPRINTFLETTER ".%d.bin",ress->job_no,num_res);
write_adcdata_separate(out, std::string(result_filename), *res);
}
break;
case base64:
write_adcdata_base64(out,*res);
break;
case csv:
write_adcdata_formated(out,"%d; %d\n",*res);
break;
case ascii:
write_adcdata_formated(out,"%d\t%d\n",*res);
break;
default:
/* forget data */
fprintf(stderr,"forgeting adc data\n");
}
num_res++;
}
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adc_to_file(const std::string& filename, const adc_result* res) const {
// fall back to some default mode
data_save_mode_type how=data_save_mode;
if (how==defaultmode) how=ascii;
FILE* out=fopen(filename.c_str(),"w");
if (out==0) {
fprintf(stderr,"could not open file %s\n",filename.c_str());
return 0;
}
fprintf(out,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
fprintf(out,"<result job=\"%" SIZETPRINTFLETTER "\">\n",res->job_no);
fwrite(res->description.c_str(),res->description.size(),1,out);
fprintf(out,"\n");
switch (how) {
case separate_file:
{
// write separate data file
write_adcdata_separate(out, filename+".bin", res);
}
break;
case base64:
write_adcdata_base64(out,res);
break;
case csv:
write_adcdata_formated(out,"%d; %d\n",res);
break;
case ascii:
write_adcdata_formated(out,"%d\t%d\n",res);
break;
default:
/* forget data */
fprintf(stderr,"forgeting adc data\n");
}
fprintf(out,"</result>\n");
fclose(out);
return 0;
}
int xml_result_writer::write_adcdata_separate(FILE* out, const std::string& datafilename, const adc_result* res) const {
/* todo: error handling */
FILE* binout=fopen(datafilename.c_str(),"wb");
if (binout==0) {
fprintf(stderr,"could not open file %s\n",datafilename.c_str());
return 0;
}
fwrite(res->data, sizeof(short int)*res->nchannels, res->samples, binout);
fclose(binout);
fprintf(out,
"<adcdatafile path=\"%s\" samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\"/>\n",
datafilename.c_str(),
res->samples,
res->sampling_frequency,
res->nchannels);
return 0;
}
int xml_result_writer::write_adcdata_formated(FILE* out, const std::string& format, const adc_result* res) const {
fprintf(out,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\n",res->samples,res->sampling_frequency, res->nchannels);
for(size_t i=0;i<res->samples;++i) {
for (int j = 0; j < res->nchannels; j++) {
fprintf(out, format.c_str(), res->data[i*2 + j]);
}
}
fprintf(out,"</adcdata>\n");
return 0;
}
int xml_result_writer::write_adcdata_base64(FILE* out, const adc_result* res) const {
fprintf(out,"<adcdata samples=\"%" SIZETPRINTFLETTER "\" rate=\"%g\" channels=\"%i\">\n",res->samples,res->sampling_frequency, res->nchannels);
unsigned int base64length=0;
XMLByte* base64buffer=XERCES_CPP_NAMESPACE_QUALIFIER Base64::encode((XMLByte*)res->data,res->samples*res->nchannels*sizeof(short int),&base64length);
fwrite(base64buffer,1,base64length,out);
XERCES_CPP_NAMESPACE_QUALIFIER XMLString::release(&base64buffer);
fprintf(out,"</adcdata>\n");
return 0;
}

158
core/xml_result.h Normal file
View File

@ -0,0 +1,158 @@
/* ******************************************************************************
author: Achim Gaedke
********************************************************************************/
#ifndef XML_RESULT_H
#define XML_RESULT_H
#include "expat.h"
#include "result.h"
#include <cstdio>
#include <string>
/**
can read result files
*/
class xml_result_reader {
std::string jobfilenamepattern;
public:
/**
instantiates parser
*/
xml_result_reader();
/**
instantiates parser
*/
xml_result_reader(const std::string& jobpattern);
#if 1
/**
returns the number of obtained adc results, and 0 if no result available
*/
result* read(size_t jobno);
/**
returns the number of obtained adc results, and 0 if no result available
*/
result* read(const std::string& filename);
#else
/**
returns the number of obtained adc results, and 0 if no result available
*/
size_t read(size_t jobno);
/**
returns NULL, if no result available, or file contains no result
*/
size_t read(const std::string& filename);
#endif
/**
returns error message or empty string, if none
*/
std::string get_error_message() const;
/**
return next adc result data, the result data are no longer available from the handler
if there is no result left, NULL is returned
*/
adc_result* get_result();
/**
frees the parser
*/
~xml_result_reader();
};
/**
writes results in xml formated files
*/
class xml_result_writer {
public:
/**
\brief the adc data store method to choose
*/
typedef enum {defaultmode, separate_file, ascii, csv, base64, hex} data_save_mode_type;
private:
/**
the chosen save method for adc data
*/
data_save_mode_type data_save_mode;
/**
the pattern for filename creation from job number by snprintf
*/
std::string resultfilename_pattern;
public:
/**
also default constructor, no resultfile name pattern
*/
xml_result_writer(data_save_mode_type how=defaultmode);
/**
also default constructor, resultfile name pattern can be specified
*/
xml_result_writer(const std::string& pattern, data_save_mode_type how=defaultmode);
/**
the result is written according to the result number and result file pattern
*/
void write(result* res);
/**
the result is written to the specified file
*/
int write_to_file(const std::string& filename, const result* res) const;
/**
write an adc result, choose the right method
*/
int write_adc_to_file(const std::string& filename, const adc_result* res) const;
/**
write an adc result, choose the right method
*/
int write_adcs_to_file(const std::string& filename, const adc_results* res) const;
/**
write data to a seperate file
*/
int write_adcdata_separate(FILE* out, const std::string& datafilename, const adc_result* res) const;
/**
write the adcdata formated, pairs of samples each line
*/
int write_adcdata_formated(FILE* out, const std::string& format, const adc_result* res) const;
/**
write the data in base64 code to file
*/
int write_adcdata_base64(FILE* out, const adc_result* res) const;
/**
write configuration tags to one file
*/
int write_configuration_results_to_file(const std::string& filename, const configuration_results& ress) const;
/**
write the error message
*/
int write_error_to_file(const std::string& filename, const error_result* res) const;
/**
if nothing is konwn, use this one
*/
int write_unknown_to_file(const std::string& filename, const result* res) const;
};
#endif /* XML_RESULT_H */

369
core/xml_states.cpp Normal file
View File

@ -0,0 +1,369 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "states.h"
#include "xml_states.h"
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <cstring>
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
void xml_state_reader_startelement_handler(xml_state_reader* self,
const XML_Char *name,
const XML_Char **atts ) {
self->start_element(name,atts);
}
void xml_state_reader_endelement_handler(xml_state_reader* self,
const XML_Char *name) {
self->end_element(name);
}
const XML_Char* xml_state_reader::search_attribute(const XML_Char** atts,const XML_Char* attr_name) const {
if (atts==NULL || atts[0]==NULL) return NULL;
size_t attr_idx=0;
while (strcmp(atts[attr_idx],attr_name)!=0) {
attr_idx+=2;
if (atts[attr_idx]==NULL) return NULL;
}
return atts[attr_idx+1];
}
const XML_Char* xml_state_reader::search_attributes(const XML_Char** atts, ...) const {
if (atts==NULL || atts[0]==NULL) return NULL;
va_list names;
va_start(names,atts);
const XML_Char* attr_name=va_arg(names, const XML_Char*);
while (attr_name!=NULL) {
size_t attr_idx=0;
while (atts[attr_idx]!=NULL && strcmp(atts[attr_idx],attr_name)!=0)
attr_idx+=2;
if (atts[attr_idx]!=NULL) {
va_end(names);
return atts[attr_idx+1];
}
attr_name=va_arg(names, const XML_Char*);
}
va_end(names);
return NULL;
}
state_atom* xml_state_reader::state_factory(const XML_Char *name,
const XML_Char **atts) const {
if (strcmp(name,"state")==0) {
state* s=new state(1);
const XML_Char* length=search_attributes(atts,"length","time",(XML_Char*)NULL);
if (length!=NULL) s->length=strtod(length,NULL);
return s;
}
if (strcmp(name,"sequent")==0) {
state_sequent* s=new state_sequent();
const XML_Char* repeat=search_attribute(atts,"repeat");
if (repeat!=NULL)
s->repeat=strtoul(repeat,0,0);
else
s->repeat=1;
return s;
}
if (strcmp(name,"parallel")==0) {
return new state_parallel();
}
if (strcmp(name,"ttlout")==0) {
ttlout* ttls=new ttlout();
const XML_Char* id=search_attributes(atts,"id","i",(XML_Char*)NULL);
if (id!=NULL) ttls->id=strtol(id,NULL,0);
const XML_Char* value=search_attribute(atts,"value");
if (value!=NULL) ttls->ttls=strtoul(value,NULL,0);
const XML_Char* channel=search_attribute(atts,"channel");
if (channel!=NULL) {
const XML_Char* state=search_attribute(atts,"state");
if (state!=NULL) {
size_t number=strtoul(channel,0,0);
char state_char=(state)[0];
if (state_char=='h' || state_char=='1')
ttls->ttls.set(number,1);
else
ttls->ttls.set(number,0);
}
else {
fprintf(stderr,"found invalid ttl state: ignoring\n");
delete ttls;
ttls=NULL;
}
}
return (state_atom*)ttls;
}
if (strcmp(name,"analogout")==0) {
analogout* aout=new analogout();
const XML_Char* channel=search_attributes(atts,"channel","c","id","i",(XML_Char*)NULL);
const XML_Char* frequency=search_attributes(atts,"frequency","f",(XML_Char*)NULL);
const XML_Char* phase=search_attributes(atts,"phase","p",(XML_Char*)NULL);
const XML_Char* amplitude=search_attributes(atts,"amplitude","a",(XML_Char*)NULL);
const XML_Char* dac_value=search_attributes(atts,"dac_value","d",(XML_Char*)NULL);
if (dac_value!=NULL) aout->dac_value=strtol(dac_value,NULL,0);
if (channel!=NULL) aout->id=strtoul(channel,NULL,0);
if (frequency!=NULL) aout->frequency=strtod(frequency,NULL);
if (phase!=NULL) aout->phase=strtod(phase,NULL);
if (amplitude!=NULL)
aout->amplitude=strtod(amplitude,NULL);
else
aout->amplitude=1.0;
return (state_atom*)aout;
}
if (strcmp(name,"analogin")==0) {
analogin* ain=new analogin();
const XML_Char* id=search_attributes(atts,"id","i",(XML_Char*)NULL);
const XML_Char* frequency=search_attributes(atts,"f","frequency",(XML_Char*)NULL);
const XML_Char* samples=search_attributes(atts,"s","samples",(XML_Char*)NULL);
const XML_Char* channels=search_attributes(atts,"c","channels",(XML_Char*)NULL);
const XML_Char* resolution = search_attributes(atts,"r","res","resolution",(XML_Char*)NULL);
if (resolution!=NULL)
ain->resolution=strtoul(resolution,NULL,0);
else
ain->resolution=14; // was 12
if (frequency!=NULL) ain->sample_frequency=strtod(frequency,NULL);
if (samples!=NULL) ain->samples=strtoul(samples,NULL,0);
if (id!=NULL) ain->id=strtoul(id,NULL,0);
if (channels!=NULL) {
ain->channels = channel_array(strtoul(channels,NULL,0));
ain->nchannels = ain->channels.count();
} else {
ain->channels = channel_array(3);
ain->nchannels = ain->channels.count();
}
// set parameters for each channel
for (int i = 0; i < ain->nchannels; i++) {
if (ain->channels[i] == true) {
char buffer1[100];
char buffer2[100];
sprintf(buffer1, "sen%i", i);
sprintf(buffer2, "sensitivity%i", i - 1);
// read sensitivity
const XML_Char* sensitivity = search_attributes(atts, buffer1, buffer2,(XML_Char*)NULL);
if (sensitivity != NULL) {
if (ain->sensitivity == NULL) {
ain->sensitivity = new double[ain->nchannels];
}
ain->sensitivity[i] = strtod(sensitivity, NULL);
} else {
ain->sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY;
}
delete sensitivity;
// read offset
sprintf(buffer1, "offset%i", i);
const XML_Char* offset = search_attributes(atts, buffer1, (XML_Char*)NULL);
if (offset != NULL) {
if (ain->offset == NULL) {
ain->offset = new int[ain->nchannels];
}
ain->offset[i] = strtol(offset, NULL, 0);
} else {
ain->offset[i] = ADC_M2I_DEFAULT_OFFSET;
}
delete offset;
// read impedance
sprintf(buffer1, "impedance%i", i);
const XML_Char* impedance = search_attributes(atts, buffer1, (XML_Char*)NULL);
if (impedance != NULL) {
if (ain->impedance == NULL) {
ain->impedance = new double[ain->nchannels];
}
ain->impedance[i] = strtol(impedance, NULL, 0);
} else {
ain->impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE;
}
delete impedance;
}
}
return (state_atom*)ain;
}
fprintf(stderr,"unknown tag %s found\n",name);
return NULL;
}
void xml_state_reader::start_element(const XML_Char *name,
const XML_Char **atts) {
state_atom* new_element=state_factory(name,atts);
if (root==NULL)
root=new_element;
if (new_element!=NULL && !pending_elements.empty()) {
state* container=dynamic_cast<state*>(pending_elements.back());
if (container!=NULL)
container->push_back(new_element);
else
fprintf(stderr,"found invalid nested element %s\n",name);
}
pending_elements.push_back(new_element);
}
void xml_state_reader::end_element(const XML_Char *name) {
pending_elements.pop_back();
}
xml_state_reader::xml_state_reader() {
fprintf(stderr, "xml_state_reader class is decrepated, please shift to xerces supporting classes.\n");
root=(state_atom*)NULL;
parser=XML_ParserCreate((XML_Char*)NULL);
XML_SetUserData(parser,(void*)this);
XML_SetElementHandler(parser,
(XML_StartElementHandler)xml_state_reader_startelement_handler,
(XML_EndElementHandler)xml_state_reader_endelement_handler);
}
state_atom* xml_state_reader::read_from_string(const std::string& data) {
if (!XML_Parse(parser,data.c_str(), data.size(),1)) {
/* handle parse error */
fprintf(stderr,"error while parsing\n");
delete root;
root=NULL;
return NULL;
}
state_atom* return_value=root;
root=NULL;
return return_value;
}
state_atom* xml_state_reader::read_from_file(const std::string& filename) {
int docfd=open(filename.c_str(),O_RDONLY);
for (;;) {
const size_t BUFF_SIZE=1<<12;
int bytes_read;
void *buff = XML_GetBuffer(parser, BUFF_SIZE);
if (buff == NULL) {
fprintf(stderr, "could not get the buffer from XML Parser\n");
close(docfd);
return NULL;
}
bytes_read = read(docfd, buff, BUFF_SIZE);
if (bytes_read < 0) {
/* handle error */
fprintf(stderr,"error while reading\n");
if (root!=NULL) delete root;
root=NULL;
break;
}
if (! XML_ParseBuffer(parser, bytes_read, bytes_read == 0)) {
/* handle parse error */
fprintf(stderr,"error while parsing\n");
if (root!=NULL) delete root;
root=NULL;
break;
}
// peaceful end
if (bytes_read == 0) break;
};
close(docfd);
state_atom* return_value=root;
root=NULL;
return return_value;
}
xml_state_reader::~xml_state_reader(){
XML_ParserFree(parser);
if (root!=NULL) delete root;
}
int xml_state_writer::write_states(FILE* output, const state_atom& states_to_write, int add_header, int indent_size) {
if (add_header) {
fprintf(output,"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
}
std::string indent_string(indent_size,' ');
const state_sequent* ss=dynamic_cast<const state_sequent*>(&states_to_write);
if (ss!=NULL) {
if (ss->repeat==1)
fprintf(output,"%s<sequent>\n",indent_string.c_str());
else
fprintf(output,"%s<sequent repeat=\"%" SIZETPRINTFLETTER "\">\n",indent_string.c_str(),ss->repeat);
for (state::const_iterator i=ss->begin();i!=ss->end(); i++) {
write_states(output,**i,0,indent_size+indent_increase);
}
fprintf(output,"%s</sequent>\n",indent_string.c_str());
return 1;
}
const state_parallel* sp=dynamic_cast<const state_parallel*>(&states_to_write);
if (sp!=NULL){
fprintf(output,"%s<parallel>\n",indent_string.c_str());
for (state::const_iterator i=sp->begin();i!=sp->end(); i++) {
write_states(output,**i,0,indent_size+indent_increase);
}
fprintf(output,"%s</parallel>\n",indent_string.c_str());
return 1;
}
const state* st=dynamic_cast<const state*>(&states_to_write);
if (st!=NULL) {
fprintf(output,"%s<state length=\"%g\">\n",indent_string.c_str(),st->length);
for (state::const_iterator i=st->begin();i!=st->end(); i++) {
write_states(output,**i,0,indent_size+indent_increase);
}
fprintf(output,"%s</state>\n",indent_string.c_str());
return 1;
}
const ttlout* ttlo=dynamic_cast<const ttlout*>(&states_to_write);
if (ttlo!=NULL) {
fprintf(output,"%s<ttlout value=\"0x%lx\"/>\n",
indent_string.c_str(),
ttlo->ttls.to_ulong());
return 1;
}
const analogout* ao=dynamic_cast<const analogout*>(&states_to_write);
if (ao!=NULL) {
fprintf(output,
"%s<analogout id=\"%d\" frequency=\"%g\" amplitude=\"%g\" phase=\"%g\" dac_value=\"%d\"/>\n",
indent_string.c_str(),
ao->id,
ao->frequency,
ao->amplitude,
ao->phase,
ao->dac_value);
return 1;
}
const analogin* ai=dynamic_cast<const analogin*>(&states_to_write);
if (ai!=NULL) {
char args[2000];
char buffer[1000];
sprintf(args, "%s<analogin id=\"%d\" samples=\"%" SIZETPRINTFLETTER "\" sample_frequency=\"%g\" channels=\"%lu\" resolution=\"%" SIZETPRINTFLETTER "\"",
indent_string.c_str(),
ai->id,
ai->samples,
ai->sample_frequency,
ai->channels.to_ulong(),
ai->resolution);
for (int i = 0; i < ai->nchannels; i++) {
sprintf(buffer, "sensitivity%d=\"%g\" offset%d=\"%d\" impedance%d=\"%g\"", i, ai->sensitivity[i], i, ai->offset[i], i, ai->impedance[i]);
strcat(args, buffer);
}
strcat(args, "/>\n");
fprintf(output, args);
return 1;
}
fprintf(output,"%s<!-- something missing -->\n",indent_string.c_str());
return 0;
}

162
core/xml_states.h Normal file
View File

@ -0,0 +1,162 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef XML_STATES_H
#define XML_STATES_H
#include "states.h"
#include "constants.h"
#include <expat.h>
#include <cstdio>
#include <xercesc/dom/DOMElement.hpp>
/**
\defgroup xmlstateinterface XML support for all state classes
\ingroup states
The state sequences consist of states and again sequences. A state is defined by a number of state_atom objects.
This definition results in a file with nested sections, that is parsed into a tree formed by the state objects.
The leaves of the tree are normaly state_atom objects. These state_atom objects define the state of a device.
An example for a sequence definition is:
\verbatim<?xml version="1.0" encoding="ISO-8859-1"?>
<sequent repeat="3">
<state time="3e-3">
<ttlout value="4"/>
<ttlout channel="2" state="0"/>
<analogout channel="1" f="1e6" p="0"/>
<analogout channel="2" f="1e6" p="90"/>
</state>
<sequent repeat="15">
<state time="3e-3">
<ttlout value="7"/>
<analogout channel="1" f="1e6" p="0"/>
<analogout channel="2" f="1e6" p="90"/>
</state>
<state time="1e-3">
<analogin f="5e6" s="4096"/>
<state/>
</sequent>
</sequent>\endverbatim
The first line of this code is a statement of xml conformity. (More about xml can be read at:
http://www.w3.org/TR/REC-xml .)
\n
The second line defines a new subsequence of states by using the tag \<sequent repeat="3"\>.
The "repeat" value 3 is the loop count. This line effects an instatiation of a state_sequent object.
All information will be collected in this branch, it is closed on the last line by \</sequent\>.
In this example the third line provides a %state tag corresponding to a state object.
So this also line specifies the duration of this state in seconds.
This section is closed with the eigth line containing \</state\>.
A xml document can contain only one top-level-element, that is called root or document element.
Applying the xml_state_reader it is not necessary to begin with a sequent element.
\n
A xml section (representing a state or sequence in our case) is opend by a tag with a name in angle braces like
\<sequent\> and is closed with an extra slash before the name \</sequent\>. Attributes can follow in the form name="value",
attributes must be quoted (single or double quotes). If such a section has no contents there is a short form with a slash
before the closing angle, e.g. \<ttlout value="7"/\>.
\n
Of course outside these tags, text can be supplied. By now, xml_state_reader ignores this text.
Inside a %state section the definition of the state is collected. Implemented are ttlout, analogout and
analogin. One element can occurr several times. How these states are merged is machine dependent.
So a state sequence is defined by <b>one sequent</b> section, containing again serval <b>state</b> or <b>sequent</b>
sections. The <b>state</b> sections must not contain other <b>state</b> sections or <b>sequent</b> sections.
*/
//@{
/**
\brief gains state sequence from an xml event stream
*/
class xml_state_reader {
state_atom* root;
std::list<state_atom*> pending_elements;
XML_Parser parser;
public:
/**
instantiates an expat xml parser object
*/
xml_state_reader();
/**
frees the parser object
*/
~xml_state_reader();
/**
\brief parses xml file containing sequences of states
the name of the file is the only parameter
\return returns tree of states or NULL if an parse error occured
*/
state_atom* read_from_file(const std::string& filename);
state_atom* read_from_dom(const XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* e) {
// to do work, work and work
return NULL;
}
/**
\brief parses a string containing sequences of states
\return returns tree of states or NULL if an parse error occured
*/
state_atom* read_from_string(const std::string& data);
/** \brief finds an attribute in the attributes array
\return returns the pointer to the attributes value or NULL if not in the array
*/
const XML_Char* search_attribute(const XML_Char** atts, const XML_Char* attr) const;
/**
\brief looks for an attribute with different names
the first alternative, that could be found is taken
\return returns a pointer to the attributes value or NULL if nothing approprate is found
*/
const XML_Char* search_attributes(const XML_Char** atts, ...) const;
/**
create new states from xml tags and their attributes
*/
state_atom* state_factory(const XML_Char* name, const XML_Char** atts) const;
/**
start tag callback for expat parser
*/
void start_element(const XML_Char* name,const XML_Char** atts);
/**
end tag callback for expat parser
*/
void end_element(const XML_Char* name);
};
/**
\ingroup states
the writer outputs states to xml files
takes care of formating
*/
class xml_state_writer {
public:
/// have a stack
std::list<state_atom*> pending_elements;
/// allow nice formating druing recursive operation
size_t indent_offset;
/// allow nice formating druing recursive operation
size_t indent_increase;
/// simple initialisation
xml_state_writer(){
indent_offset=0;
indent_increase=2;
};
/**
write states to a given output
if add_header is !=0 a full xml document is given
*/
int write_states(FILE* output, const state_atom& states_to_write, int add_header=0, int indent_size=0);
};
//@}
#endif

282
doc/Doxyfile Normal file
View File

@ -0,0 +1,282 @@
# Doxyfile 1.5.5
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "\"DAMARIS\" - Backends"
PROJECT_NUMBER = 0.11
OUTPUT_DIRECTORY =
CREATE_SUBDIRS = YES
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
TYPEDEF_HIDES_STRUCT = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = YES
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../core/ \
../drivers/ \
../drivers/SpinCore-PulseBlaster/pulseblaster.c \
../drivers/SpinCore-PulseBlaster/pulseblaster.h \
../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h \
../drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp \
../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h \
../drivers/SpinCore-PulseBlaster/PulseBlasterProgram.cpp \
../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.h \
../drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp \
../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h \
../drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.cpp \
../drivers/Eurotherm-2000Series/Eurotherm-2000Series.h \
../drivers/Eurotherm-2000Series/Eurotherm-2000Series.cpp \
../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h \
../drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.cpp \
../drivers/Spectrum-MI40xxSeries/ \
../drivers/Spectrum-MI40xxSeries/GatedData.h \
../drivers/Spectrum-MI40xxSeries/GatedData.cpp \
../drivers/TiePie-HS3/TiePie-HS3.h \
../drivers/TiePie-HS3/TiePie-HS3.cpp \
../drivers/Datel-PCI416/Datel-PCI416.h \
../drivers/Datel-PCI416/Datel-PCI416.cpp \
../drivers/PTS-Synthesizer/PTS.cpp \
../drivers/PTS-Synthesizer/PTS.h \
../drivers/Tecmag-DAC20/DAC20.cpp \
../drivers/Tecmag-DAC20/DAC20.h \
../drivers/dummy \
../machines/ \
../tests/ \
../tests/stefan/ \
/home/achim/build/backends/machines/bg_backend.cpp \
/home/achim/build/backends/machines/deuteron_backend.cpp \
/home/achim/build/backends/machines/dummycore.cpp \
/home/achim/build/backends/machines/hardware.cpp \
/home/achim/build/backends/machines/hardware.h \
/home/achim/build/backends/machines/magnexgrad_backend.cpp \
/home/achim/build/backends/machines/magnexgrad_backend_dds.cpp \
/home/achim/build/backends/machines/Mobilecore.cpp \
/home/achim/build/backends/machines/Mobile_wo_sync_backend.cpp \
/home/achim/build/backends/machines/NQRcore.cpp \
/home/achim/build/backends/machines/PFGcore.cpp \
/home/achim/build/backends/machines/bg_backend.cpp
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.cpp
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH = .
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH = .
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = backends-html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER = footer.html
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
HTML_DYNAMIC_SECTIONS = NO
CHM_FILE = backends.chm
HHC_LOCATION =
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = YES
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
DOT_PATH = /opt/graphviz-2.2.1/bin
DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = YES
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

30
doc/Makefile Normal file
View File

@ -0,0 +1,30 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
DOXYGEN=doxygen
PATH:=$(PATH)
.PHONY: all install docinstall clean doc
all:
install:
doc: backends-html
backends-html: .htmltag
software_design_overview_scaled.png: software_design_overview.png
convert -resize 635x476 $< $@
.htmltag: software_design_overview_scaled.png
$(DOXYGEN) Doxyfile && touch .htmltag
clean:
rm -rf backends-html software_design_overview_scaled.png .htmltag *~ \#*
docinstall: backends-html
install -d $(PREFIX)/doc && cp -r backends-html/* $(PREFIX)/doc

33
doc/experiment_job.xml Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<experiment id="0">
<!-- saturation recovery -->
<sequent>
<!-- saturation comb -->
<sequent>
<!-- suppose, that gate is on channel 1 -->
<!-- suppose, that pulse is on channel 2 -->
<state time="100e-6"><ttlout value="1"/><analogout f="1e7"/></state>
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
<state time="2e-6"><ttlout value="1"/><analogout f="1e7"/></state>
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
<state time="1e-6"><ttlout value="1"/><analogout f="1e7"/></state>
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
<state time="1e-6"><ttlout value="1"/><analogout f="1e7"/></state>
<state time="5e-6"><ttlout value="3"/><analogout f="1e7"/></state>
</sequent>
<!-- evolution time -->
<sequent>
<state time="1e-3"/>
<state time="100e-6"><ttlout value="1"/><analogout f="1e7"/></state>
</sequent>
<!-- ninty degree pulse and data recording after dead time -->
<sequent>
<state time="5e-6"><ttlout value="3"/><analogout f="1e5"/></state>
<state time="1e-6"><ttlout value="0"/><analogout f="1e5"/></state>
<state time="1e-2">
<analogin s="2000" f="2e6" resolution="12" sensitivity="4"/>
<analogout f="1e5"/>
</state>
</sequent>
</sequent>
</experiment>

6
doc/footer.html Normal file
View File

@ -0,0 +1,6 @@
<p>
<hr><br>
$projectname, version $projectnumber: back to <a href="main.html">mainpage</a>
</p>
</body>
</html>

3
doc/quit_job.xml Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<quit/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

3
doc/wait_job.xml Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<wait length="120"/>

64
drivers/ADC.h Normal file
View File

@ -0,0 +1,64 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef ADC_H
#define ADC_H
#include <cstdlib>
#include <string>
#include "core/result.h"
#include "core/states.h"
#include "core/core_exception.h"
/**
\defgroup drivers Drivers
*/
/**
\defgroup basedrivers Drivers' Base Classes
\ingroup drivers
*/
//@{
/**
* ADC exception
*/
class ADC_exception: public RecoverableException
{
public:
explicit ADC_exception(const std::string& msg) throw (): RecoverableException(msg) {}
explicit ADC_exception(const char* msg) throw (): RecoverableException(msg) {}
virtual ~ADC_exception() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (ADC_exception): "; }
};
/**
base class for adc drivers
*/
class ADC
{
public:
/**
start sampling after trigger and return field of int
*/
virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity = 5.0, size_t resolution = 14)=0;
/**
here the data aquisition unit is configured and adds the necessary pusle components to the program
*/
virtual void set_daq(state& exp)=0;
/**
read the sample data
*/
virtual result* get_samples(double timeout = 0.0)=0;
virtual ~ADC()
{
}
};
//@}
#endif

View File

@ -0,0 +1,296 @@
///////////////////////////////////////////////////////////////////////////////
//
// 41632dll.h: Function definition of pci41632.dll exported functions
// See pci416io.h, pci416libw95.h and pci416_32dll.h
// for the description of each function.
// Copyright (c) Datel, Inc. 1997
// Platform: Win32, Win95
// Compiler: MVC4.0
// Version: 3.0
// Author: GS
// created: 4/16/97
// modified: 7/15/97
///////////////////////////////////////////////////////////////////////////////
#ifndef _41632DLL_H
#define _41632DLL_H
#include <windows.h>
#include "pcilib32.h"
#include "pci416df.h"
#ifndef LIB_TYPE
#define LIB_TYPE WINAPI
#endif
// function description -> see pci416io.h
typedef DWORD (LIB_TYPE *TFP_pci416_init)(DWORD *brdcount);
typedef DWORD (LIB_TYPE *TFP_pci416_close)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_find_pci_device)(WORD busno, WORD venid,
WORD deviceid, WORD bufsize,
BYTE *buf);
typedef DWORD (LIB_TYPE *TFP_get_pci_device_info)(WORD busno, WORD devno,
PCI_CONFIG_SPACE *conf);
typedef DWORD (LIB_TYPE *TFP_write_pci_port)(DWORD portadr, DWORD data);
typedef DWORD (LIB_TYPE *TFP_read_pci_port)(DWORD portadr, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_count)(DWORD *brdcount);
typedef DWORD (LIB_TYPE *TFP_pci416_get_cm_devnodes)(INT brdindex,
WORD bufsize,
DWORD *devnodebuf);
typedef DWORD (LIB_TYPE *TFP_pci416_get_cmdevinf)(DWORD brdindex,
PCI_CONFIG_SPACE *conf);
typedef DWORD (LIB_TYPE *TFP_pci416_get_badr)(DWORD brdindex, WORD bufsize,
WORD *badrbuf);
typedef DWORD (LIB_TYPE *TFP_pci416_getcaps)(DWORD brdindex, WORD bufsize, DWORD *buf);
typedef DWORD (LIB_TYPE *TFP_pci416_set_cmdreg)(DWORD brdindex, WORD mode,
DWORD regval, DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_read_statusreg)(DWORD brdindex, DWORD *regval);
typedef DWORD (LIB_TYPE *TFP_pci416_set_smplcntr)(DWORD brdindex, DWORD samples);
typedef DWORD (LIB_TYPE *TFP_pci416_set_chanadr)(DWORD brdindex, WORD mode,
DWORD regval, DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_clear_fifo)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_pci416_enablead)(DWORD brdindex, BOOL enable);
typedef DWORD (LIB_TYPE *TFP_pci416_set_pllreg)(DWORD brdindex, DWORD valA, DWORD valN);
typedef DWORD (LIB_TYPE *TFP_pci416_read_fifo)(DWORD brdindex, WORD count, DWORD *buf);
typedef DWORD (LIB_TYPE *TFP_pci416_set_timer)(DWORD brdindex, WORD mode,
WORD counter02, WORD counter1);
typedef DWORD (LIB_TYPE *TFP_pci416_set_portctrreg)(DWORD brdindex, DWORD regval);
typedef DWORD (LIB_TYPE *TFP_pci416_get_portctrreg)(DWORD brdindex, DWORD *regval);
typedef DWORD (LIB_TYPE *TFP_pci416_read_port)(DWORD brdindex, WORD port, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_write_port)(DWORD brdindex, WORD port, DWORD data);
typedef DWORD (LIB_TYPE *TFP_pci416_write_dac)(DWORD brdindex, WORD data);
typedef DWORD (LIB_TYPE *TFP_pci416_fifo_status)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_check_fifohf)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_setup_dma)(DWORD brdindex, DWORD mode,
DWORD *bufsize, DWORD *hndDMAbuf);
typedef DWORD (LIB_TYPE *TFP_pci416_stop_dma)(DWORD brdindex, DWORD *tcount);
typedef DWORD (LIB_TYPE *TFP_pci416_reload_dma)(DWORD brdindex, DWORD bufno,
DWORD *bufsize, DWORD *hndDMAbuf);
typedef DWORD (LIB_TYPE *TFP_pci416_dma_status)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_read_intcsrreg)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_set_intcsrreg)(DWORD brdindex, WORD mode,
DWORD regval, DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_read_mcsrreg)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_set_mcsrreg)(DWORD brdindex, WORD mode,
DWORD regval, DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_read_mwarreg)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_set_mwarreg)(DWORD brdindex, WORD mode, DWORD regval,
DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_read_mwtcreg)(DWORD brdindex, DWORD *data);
typedef DWORD (LIB_TYPE *TFP_pci416_set_mwtcreg)(DWORD brdindex, WORD mode, DWORD regval,
DWORD *shregval);
typedef DWORD (LIB_TYPE *TFP_pci416_copy_dmabuffer)(DWORD brdindex, DWORD bufno,
DWORD start, DWORD *count,
DWORD *pDest );
typedef DWORD (LIB_TYPE *TFP_pci416_get_dmabuf_hndl)(DWORD brdindex, DWORD bufno,
DWORD offset, DWORD *pHndl,
DWORD *bufsize);
typedef DWORD (LIB_TYPE *TFP_pci416_pause_resume_dma)(DWORD brdindex, DWORD flags);
typedef DWORD (LIB_TYPE *TFP_pci416_getError)(LPTSTR *str);
// function description -> see pci416libw95.h
typedef INT (LIB_TYPE *TFP_get_adm_stats)(DWORD brdindex,
ADM_STATS *admstats);
typedef DWORD (LIB_TYPE *TFP_read_pt_port_control_reg)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_write_pt_port_control_reg)(DWORD brdindex, UINT val);
typedef DWORD (LIB_TYPE *TFP_read_pt_port_data)(DWORD brdindex, UINT prt);
typedef DWORD (LIB_TYPE *TFP_write_pt_port_data)(DWORD brdindex, UINT prt, UINT val);
typedef void (LIB_TYPE *TFP_enable_pt_dma_logic)(DWORD brdindex, UINT md);
typedef DWORD (LIB_TYPE *TFP_start_stop_pt_dma_logic)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_set_pt_trigger_mode)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_set_pt_interrupt_mode)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_enable_pt_eos_interrupt)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_enable_pt_half_full_interrupt)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_read_pt_interrupt_status)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_pt_scan_select)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_set_pt_auto_increment)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_set_pt_marker_select)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_set_pt_pretrigger)(DWORD brdindex, UINT md);
typedef void (LIB_TYPE *TFP_disable_pt_interrupt)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_clear_pt_interrupt)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_enable_pt_interrupt)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_pt_ad_clock_source)(DWORD brdindex, UINT src);
typedef void (LIB_TYPE *TFP_set_pt_adm_control)(DWORD brdindex, UINT val);
typedef void (LIB_TYPE *TFP_set_pt_command_reg)(DWORD brdindex,DWORD val);
typedef DWORD (LIB_TYPE *TFP_read_pt_status_reg)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_pt_fifo_empty)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_pt_fifo_half_full)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_pt_fifo_full)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_read_pt_arm_ff)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_read_pt_analog_trigger_status)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_read_pt_acquire_status)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_read_pt_adm)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_pt_sample_count)(DWORD brdindex, DWORD count);
typedef void (LIB_TYPE *TFP_set_pt_ad_channel)(DWORD brdindex, UINT chan);
typedef void (LIB_TYPE *TFP_set_pt_ad_scan_count)(DWORD brdindex, UINT count);
typedef void (LIB_TYPE *TFP_set_pt_led)(DWORD brdindex, UINT val);
typedef void (LIB_TYPE *TFP_reset_pt_fifos)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_pt_convert_enable)(DWORD brdindex, UINT val);
typedef UINT (LIB_TYPE *TFP_set_pt_pll)(DWORD brdindex, REALTYPE freq);
typedef int (LIB_TYPE *TFP_set_pt_ad_clock_rate)(DWORD brdindex,REALTYPE fs);
typedef UINT (LIB_TYPE *TFP_set_pt_trigger_rate)(DWORD brdindex,REALTYPE ft,
REALTYPE *actual);
typedef void (LIB_TYPE *TFP_generate_single_internal_trigger)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_reset_pt_trigger_timer)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_pt_dac_voltage)(DWORD brdindex, REALTYPE volts);
typedef void (LIB_TYPE *TFP_set_pt_dac_code)(DWORD brdindex, UINT code);
typedef DWORD (LIB_TYPE *TFP_read_pt_fifo)(DWORD brdindex);
typedef void (LIB_TYPE *TFP_read_pt_fifo_many)(DWORD brdindex, LPDWORD buffer,
DWORD count);
typedef void (LIB_TYPE *TFP_reset_dma_fifo)( DWORD brdindex);
typedef void (LIB_TYPE *TFP_set_dma_request_half_full)( DWORD brdindex,UINT md);
typedef void (LIB_TYPE *TFP_enable_dma_transfers)( DWORD brdindex,UINT enable);
typedef void (LIB_TYPE *TFP_set_dma_transfer_count)(DWORD brdindex, DWORD bytes);
typedef DWORD (LIB_TYPE *TFP_read_dma_transfer_count)(DWORD brdindex);
// NT only
typedef DWORD (LIB_TYPE *TFP_start_daq_irq)(DWORD brdindex,DWORD TriggerMode,
REALTYPE *TriggerRate);
typedef DWORD (LIB_TYPE *TFP_pci416_close_dmabuf_hndl)(DWORD brdindex);
typedef DWORD (LIB_TYPE *TFP_pci416_map_dmabuf)(DWORD brdindex, DWORD *pHndl, DWORD size);
typedef DWORD (LIB_TYPE *TFP_pci416_unmap_dmabuf)(DWORD brdindex, DWORD Hndl);
// function description -> see pci416_32dll.h
typedef DWORD (LIB_TYPE *TFP_get_adm_inf) (DWORD brdindex, WORD *model,
WORD *bits, WORD *channels,
WORD *shortcycle,
REALTYPE *fmax_single, REALTYPE *fmax_scan);
typedef DWORD (LIB_TYPE *TFP_set_modes) (DWORD brdindex,
WORD ClockSrc,
REALTYPE SampleRate,
DWORD SamplesPerTrigger,
WORD TriggerSrc,
WORD Channel,
WORD PreTrigger,
WORD Scan,
WORD Marker);
typedef DWORD (LIB_TYPE *TFP_start_daq)(DWORD brdindex, WORD TriggerMode,
REALTYPE *TriggerRate);
typedef DWORD (LIB_TYPE *TFP_stop_daq)(DWORD brdindex);
typedef INT (LIB_TYPE *TFP_scan_status)(DWORD brdindex,
WORD ClockSrc,
REALTYPE SampleRate,
DWORD TotalSamples,
DWORD SamplesPerTrigger,
DWORD Frames,
WORD TriggerSrc,
REALTYPE TriggerRate,
WORD Channel,
BOOL PreTrigger,
BOOL Scan,
BOOL Marker,
PVOID buffer);
/* function instance definition
assign function by loading the address from DLL with
func_name = (TFP_func_name)GetProcAddress(hModule, "func_name");
*/
TFP_pci416_init pci416_init;
TFP_pci416_close pci416_close;
TFP_find_pci_device find_pci_device;
TFP_get_pci_device_info get_pci_device_info;
TFP_write_pci_port write_pci_port;
TFP_read_pci_port read_pci_port;
TFP_pci416_count pci416_count;
TFP_pci416_get_cm_devnodes pci416_get_cm_devnodes;
TFP_pci416_get_cmdevinf pci416_get_cmdevinf;
TFP_pci416_get_badr pci416_get_badr;
TFP_pci416_getcaps pci416_getcaps;
TFP_pci416_set_cmdreg pci416_set_cmdreg;
TFP_pci416_read_statusreg pci416_read_statusreg;
TFP_pci416_set_smplcntr pci416_set_smplcntr;
TFP_pci416_set_chanadr pci416_set_chanadr;
TFP_pci416_clear_fifo pci416_clear_fifo;
TFP_pci416_enablead pci416_enablead;
TFP_pci416_set_pllreg pci416_set_pllreg;
TFP_pci416_read_fifo pci416_read_fifo;
TFP_pci416_set_timer pci416_set_timer;
TFP_pci416_set_portctrreg pci416_set_portctrreg;
TFP_pci416_get_portctrreg pci416_get_portctrreg;
TFP_pci416_read_port pci416_read_port;
TFP_pci416_write_port pci416_write_port;
TFP_pci416_write_dac pci416_write_dac;
TFP_pci416_fifo_status pci416_fifo_status;
TFP_pci416_check_fifohf pci416_check_fifohf;
TFP_pci416_setup_dma pci416_setup_dma;
TFP_pci416_stop_dma pci416_stop_dma;
TFP_pci416_reload_dma pci416_reload_dma;
TFP_pci416_dma_status pci416_dma_status;
TFP_pci416_read_intcsrreg pci416_read_intcsrreg;
TFP_pci416_set_intcsrreg pci416_set_intcsrreg;
TFP_pci416_read_mcsrreg pci416_read_mcsrreg;
TFP_pci416_set_mcsrreg pci416_set_mcsrreg;
TFP_pci416_read_mwarreg pci416_read_mwarreg;
TFP_pci416_set_mwarreg pci416_set_mwarreg;
TFP_pci416_read_mwtcreg pci416_read_mwtcreg;
TFP_pci416_set_mwtcreg pci416_set_mwtcreg;
TFP_pci416_copy_dmabuffer pci416_copy_dmabuffer;
TFP_pci416_get_dmabuf_hndl pci416_get_dmabuf_hndl;
TFP_pci416_pause_resume_dma pci416_pause_resume_dma;
TFP_pci416_close_dmabuf_hndl pci416_close_dmabuf_hndl;
TFP_pci416_map_dmabuf pci416_map_dmabuf;
TFP_pci416_unmap_dmabuf pci416_unmap_dmabuf;
TFP_pci416_getError pci416_getError;
TFP_get_adm_stats get_adm_stats;
TFP_read_pt_port_control_reg read_pt_port_control_reg;
TFP_write_pt_port_control_reg write_pt_port_control_reg;
TFP_read_pt_port_data read_pt_port_data;
TFP_write_pt_port_data write_pt_port_data;
TFP_enable_pt_dma_logic enable_pt_dma_logic;
TFP_start_stop_pt_dma_logic start_stop_pt_dma_logic;
TFP_set_pt_trigger_mode set_pt_trigger_mode;
TFP_set_pt_interrupt_mode set_pt_interrupt_mode;
TFP_enable_pt_eos_interrupt enable_pt_eos_interrupt;
TFP_enable_pt_half_full_interrupt enable_pt_half_full_interrupt;
TFP_read_pt_interrupt_status read_pt_interrupt_status;
TFP_set_pt_scan_select set_pt_scan_select;
TFP_set_pt_auto_increment set_pt_auto_increment;
TFP_set_pt_marker_select set_pt_marker_select;
TFP_set_pt_pretrigger set_pt_pretrigger;
TFP_disable_pt_interrupt disable_pt_interrupt;
TFP_clear_pt_interrupt clear_pt_interrupt;
TFP_enable_pt_interrupt enable_pt_interrupt;
TFP_set_pt_ad_clock_source set_pt_ad_clock_source;
TFP_set_pt_adm_control set_pt_adm_control;
TFP_set_pt_command_reg set_pt_command_reg;
TFP_read_pt_status_reg read_pt_status_reg;
TFP_pt_fifo_empty pt_fifo_empty;
TFP_pt_fifo_half_full pt_fifo_half_full;
TFP_pt_fifo_full pt_fifo_full;
TFP_read_pt_arm_ff read_pt_arm_ff;
TFP_read_pt_analog_trigger_status read_pt_analog_trigger_status;
TFP_read_pt_acquire_status read_pt_acquire_status;
TFP_read_pt_adm read_pt_adm;
TFP_set_pt_sample_count set_pt_sample_count;
TFP_set_pt_ad_channel set_pt_ad_channel;
TFP_set_pt_ad_scan_count set_pt_ad_scan_count;
TFP_set_pt_led set_pt_led;
TFP_reset_pt_fifos reset_pt_fifos;
TFP_set_pt_convert_enable set_pt_convert_enable;
TFP_set_pt_pll set_pt_pll;
TFP_set_pt_ad_clock_rate set_pt_ad_clock_rate;
TFP_set_pt_trigger_rate set_pt_trigger_rate;
TFP_generate_single_internal_trigger generate_single_internal_trigger;
TFP_reset_pt_trigger_timer reset_pt_trigger_timer;
TFP_set_pt_dac_voltage set_pt_dac_voltage;
TFP_set_pt_dac_code set_pt_dac_code;
TFP_read_pt_fifo read_pt_fifo;
TFP_read_pt_fifo_many read_pt_fifo_many;
TFP_reset_dma_fifo reset_dma_fifo;
TFP_set_dma_request_half_full set_dma_request_half_full;
TFP_enable_dma_transfers enable_dma_transfers;
TFP_set_dma_transfer_count set_dma_transfer_count;
TFP_read_dma_transfer_count read_dma_transfer_count;
TFP_get_adm_inf get_adm_inf;
TFP_set_modes set_modes;
TFP_start_daq start_daq;
TFP_start_daq_irq start_daq_irq;
TFP_stop_daq stop_daq;
TFP_scan_status scan_status;
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
///////////////////////////////////////////////////////////////////////////////
//
// pci416_32dll.h: Header file for DLL functions that perform board setup and
// data acquisition using FIFO polling.
// Copyright (c) Datel, Inc. 1997
// Platform: Win95, Win NT
// Compiler: MVC4.0 + DDK
// Version: 3.0
// Author: GS
// created: 2/28/97
// modified: 7/9/98
///////////////////////////////////////////////////////////////////////////////
#ifndef PCI416_32DLL_H
#define PCI416_32DLL_H
#include <windows.h>
// REALTYPE is defined as double (size: 8 bytes)
/******************************************************************************
Function: get_adm_inf
Description: Returns specific info of installed A/D module.
Use this function instead the get_adm_stats function for DLL calls.
The get_adm_stats function expects a pointer to an ADM_STATS
structure which is only available in C programs. Other compilers and
programs may generate structures with different size and boundaries
that could cause sytem crashes!
Inputs: DWORD brdindex : Index of board.
Max. index = number of PCI-416 boards - 1
Char *model : ADM model
WORD *bits : Resolution in bits
WORD *channels : no.of channels
WORD *shortcycle : 1== can be short cycled
REALTYPE *fmax_single: max. sample freq. single channel mode
REALTYPE *fmax_scan: max. sample freq. multi channel mode
return: Error status as define in winerror.h
NOERROR
ERROR_INVALID_HANDLE
ERROR_DEV_NOT_EXIST
******************************************************************************/
DWORD LIB_TYPE get_adm_inf(DWORD brdindex, WORD *model, WORD *bits, WORD *channels,
WORD *shortcycle, REALTYPE *fmax_single, REALTYPE *fmax_scan);
/******************************************************************************
Function: set_modes
Description: Setup board paramters: sample rate, trigger rate, clock and trigger
source etc.
Inputs: DWORD brdindex : Index of board.
Max. index = number of PCI-416 boards - 1
WORD ClockSrc : 0= INTERNAL, 1= EXTERNAL,
other : internal with sample rate set to
max. value in scan mode.
REALTYPE SampleRate : A/D sample rate, if src set to INTERNAL
If ClockSrc is not set to INTERNAL or
EXTERNAL the sample rate is set to
max. value in scan mode.
DWORD SamplesPerTrigger: Number of samples to collect
at each trigger pulse.
WORD TriggerSrc : 0= INTERNAL,1 = EXTERNAL,
2= ANALOG Rising Edge, 3= ANALOG Falling Edge,
other: INTERNAL
WORD Channel : channel of acquisition in single channel mode
or last scan channel in scan mode if applicable
0 <= Channel < number of channels
WORD PreTrigger : 1== PreTrigger
WORD Scan : 1== Scan mode
WORD Marker : 1== set marker bit
return: Error status as define in winerror.h
NOERROR
ERROR_INVALID_HANDLE
ERROR_DEV_NOT_EXIST
******************************************************************************/
DWORD LIB_TYPE set_modes (DWORD brdindex,
WORD ClockSrc,
REALTYPE SampleRate,
DWORD SamplesPerTrigger,
WORD TriggerSrc,
WORD Channel,
WORD PreTrigger,
WORD Scan,
WORD Marker);
/******************************************************************************
Function: start_daq
Description: Starts data acquisition.
All registers have to be set prior this function call with
set_mode!
Inputs: DWORD brdindex : Index of board.
Max. index = number of PCI-416 boards - 1
WORD TriggerMode : 0== PreTrigger, external trigger,
1== Single Internal Trigger,
2== Multi Internal Trigger
other default to single trigger
REALTYPE *TriggerRate: trigger rate for multi trigger
Output: REALTYPE *TriggerRate: actual trigger rate set
Note: If PreTrigger was selected in set_modes only an external trigger
is valid. Calls other than start_daq(brdindex, 0) will result in
invalid data.
On the other hand if PreTrigger was NOT set in set_modes
a start_daq(brdindex, 0) call does not start the A/D conversion.
return: Error status as define in winerror.h
NOERROR
******************************************************************************/
DWORD LIB_TYPE start_daq(DWORD brdindex, WORD TriggerMode,
REALTYPE *TriggerRate);
/******************************************************************************
Function: stop_daq
Description: Stops data acquisition.
Inputs: DWORD brdindex : Index of board.
Max. index = number of PCI-416 boards - 1
return: Error status as define in winerror.h
NOERROR
******************************************************************************/
DWORD LIB_TYPE stop_daq(DWORD brdindex);
/******************************************************************************
Function: scan_status
Description: Acquires data by polling on the FIFO half full flag.
Inputs: DWORD brdindex : Index of board.
Max. index = number of PCI-416 boards - 1
WORD ClockSrc : C_INTERNAL, C_EXTERNAL,
default : internal with sample rate set to
max. value in scan mode.
REALTYPE SampleRate : A/D sample rate, if src set to C_INTERNAL
If ClockSrc is not set to C_INTERNAL or
C_EXTERNAL the sample rate is set to
max. value in scan mode.
DWORD TotalSamples : Total number of samples to collect
DWORD SamplesPerTrigger: Number of samples to collect
at each trigger pulse.
DWORD Frames : Number of triggers.
WORD TriggerSrc : T_INTERNAL,T_EXTERNAL,T_ANALOG,
default: T_INTERNAL
REALTYPE TriggerRate : Trigger rate
WORD Channel : channel of acquisition in single channel mode
or last scan channel in scan mode if applicable
0 <= Channel < number of channels
BOOL PreTrigger : TRUE== PreTrigger
BOOL Scan : TRUE== Scan mode
BOOL Marker : TRUE== set marker bit
PVOID buffer : User buffer to store the A/D data
return: state of the FIFO Full flag ( 0 = No overflow, 1 = FIFO FULL (overflow)
-1 error -> retreive error with pci416_getError
******************************************************************************/
INT LIB_TYPE scan_status (DWORD brdindex,
WORD ClockSrc,
REALTYPE SampleRate,
DWORD TotalSamples,
DWORD SamplesPerTrigger,
DWORD Frames,
WORD TriggerSrc,
REALTYPE TriggerRate,
WORD Channel,
BOOL PreTrigger,
BOOL Scan,
BOOL Marker,
PVOID buffer);
#endif

View File

@ -0,0 +1,477 @@
///////////////////////////////////////////////////////////////////////////////
// pci416libw95.h: Function definitions for Datel PCI-416 Control library
// for both the S593X PCI controller and the pass thru
// data acq.logic
// Copyright (c) Datel, Inc. 1997
// Platform: Win95
// Compiler: Microsoft Visual C++ V4.0
// Version: 1.0
// Author: GS
// created: 3/3/97
// modified: 4/16/97
// Comment: Ports DOS p416_lib to WIN32
// TYPE_BOARD_CONFIG and TYPE_DAQ_CONFIG structures are obsolete
// and not supported.
// Boards are accessed by a simple board index number.
// A number of functions from the DOS library are not ported
// to Windows. These functions are marked as obsolete. See below.
/////////////////////////////////////////////////////////////////////////////
#ifndef P416LIBW95_H
#define P416LIBW95_H
#ifndef LIB_TYPE
#define LIB_TYPE WINAPI
#endif
#ifndef REALTYPE
#define REALTYPE double
#endif
/******************************************************************************
Function get_adm_stats
Returns the ADM characteristics.
Parameters:
Input:
DWORD brdindex: index of board
Output:
ADM_STATS *admstats : Pointer to ADM_STATS structure that gets the data.
Return value: 0 -> no Error
-1 -> Error retreive error with pci416_getError
*******************************************************************************/
INT LIB_TYPE get_adm_stats(DWORD brdindex, ADM_STATS *admstats);
/*****************************************************************************
PASS THROUGH (A/D control)
*****************************************************************************/
///////////////////////////////////////////////////////////////////////////////
// reads 8255 port control register
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_port_control_reg(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// set 8255 port control register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE write_pt_port_control_reg(DWORD brdindex, UINT val);
///////////////////////////////////////////////////////////////////////////////
// Reads from one of the 8255 I/O registers -> 0=a, 1=b, 2=c
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_port_data(DWORD brdindex, UINT prt);
///////////////////////////////////////////////////////////////////////////////
// write to one of the 8255 I/O registers -> 0=a, 1=b, 2=c
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE write_pt_port_data(DWORD brdindex, UINT prt, UINT val);
///////////////////////////////////////////////////////////////////////////////
// Enables / Disables auto PT(A/D Data) FIFO to Bridge fifo
//
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE enable_pt_dma_logic(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Suspends / Resumes auto PT(A/D Data) FIFO to Bridge fifo
// Returns 1 in case of time out otherwise 0
// time out count is set to 100000
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE start_stop_pt_dma_logic(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Sets the trigger mode field in the P.thru command register
// 0 - internal , 1 - ext. digital, 2 - ext. analog rising edge,
// 3 - ext. analog falling edge
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_trigger_mode(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Sets the Pass thru interrupt condition
// 0 - FIFO HF, 1 - End of Scan
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_interrupt_mode(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// enables pass thru to PCI interrupt on end of scan
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE enable_pt_eos_interrupt(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// enables pass thru to PCI interrupt on half full
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE enable_pt_half_full_interrupt(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if a HF or EOS PT interrupt occured
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_interrupt_status(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Sets/Resets the Pass Thru A/D scan select bit (used for ADM model A)
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_scan_select(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Sets/resets the Pass thru A/D channel auto-increment bit
// 1 - Autoincrement, 0 - nope
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_auto_increment(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Sets/resets the Pass thru A/D Marker mode select bit
// 1 - mark, 0 - no marker
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_marker_select(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Enables/Disables the Pass thru A/D Pre-trigger mode
// 1 - Pre-trigger, 0 - no pre_trigger
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_pretrigger(DWORD brdindex, UINT md);
///////////////////////////////////////////////////////////////////////////////
// Disables the Pass thru to Host(PCI) interrupt
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE disable_pt_interrupt(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Clears the PCI interrupt on the S5933 Bridge
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE clear_pt_interrupt(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Enables the Pass thru to Host(PCI) interrupt
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE enable_pt_interrupt(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Set A/D clock source MUX
// FREQ SYNTH DIRECT --> A/D
// 0 20 - 40 MHz 2.5Khz res
// 1 10 - 20 Mhz 1.25
// 2 5 - 10 625Hz res
// 3 Freq SYNTH -> 82C54 -> A/D
// useful for exact freq < 5 Mhz
// OTHERS
// 4 External Clock
// 5 20MHz x-tal -->a/d
// 6 10Mhz x-tal -->a/d
// 7 10 Mhz -> 8254-> a/d
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_ad_clock_source(DWORD brdindex, UINT src);
///////////////////////////////////////////////////////////////////////////////
// Set A/D control bits
//
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_adm_control(DWORD brdindex, UINT val);
///////////////////////////////////////////////////////////////////////////////
// Sets pass thru command register to desired value
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_command_reg(DWORD brdindex,DWORD val);
/* obsolete use pci416_set_cmdreg(brdindex, mode, val, &retval) instead
///////////////////////////////////////////////////////////////////////////////
// Write current variable value to pass thru command register hardware
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE update_pt_command_reg(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Write to the Pass-Thru Address Register Latch (ARL)
// sets hardware and software value of pt address (register select) register
///////////////////////////////////////////////////////////////////////////////
// obsolete, use write_pci_port(DWORD portadr, DWORD data) instead
// get portadr with pci416_get_badr(DWORD brdindex, WORD *badrbuf, WORD bufsize);
void LIB_TYPE set_pt_address_reg(DWORD brdindex,DWORD addr);
*/
///////////////////////////////////////////////////////////////////////////////
// Reads the PT. hardware status register
// returns 0xFFFFFFFF in case of error
// use pci416_getError to retreive error code
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_status_reg(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if pass thru fifo is empty
// returns 0xFFFFFFFF in case of error
// use pci416_getError to retreive error code
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE pt_fifo_empty(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if pass thru fifo is half full
// returns 0xFFFFFFFF in case of error
// use pci416_getError to retreive error code
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE pt_fifo_half_full(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if pass thru fifo is full
// returns 0xFFFFFFFF in case of error
// use pci416_getError to retreive error code
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE pt_fifo_full(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if data PT A/D ARM FF is SET
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_arm_ff(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if ANATRG bit of PT -SR is high
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_analog_trigger_status(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 if data ACQ bit in PT -CR is high
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_acquire_status(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns the AD Module given by DIP setting
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_adm(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Set A/D sample count register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_sample_count(DWORD brdindex, DWORD count);
///////////////////////////////////////////////////////////////////////////////
// Set A/D Channel field of pt channel register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_ad_channel(DWORD brdindex, UINT chan);
///////////////////////////////////////////////////////////////////////////////
// Set A/D Scan Count field of channel register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_ad_scan_count(DWORD brdindex, UINT count);
///////////////////////////////////////////////////////////////////////////////
// Sets Test LED bit of channel register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_led(DWORD brdindex, UINT val);
///////////////////////////////////////////////////////////////////////////////
// Reset the PT. A/D Fifo
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE reset_pt_fifos(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Set/Reset PT. A/D convert enable bit
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_convert_enable(DWORD brdindex, UINT val);
///////////////////////////////////////////////////////////////////////////////
// Sets output clock frequency of PLL hardware
// And sets A/D clock source to PLL output
///////////////////////////////////////////////////////////////////////////////
UINT LIB_TYPE set_pt_pll(DWORD brdindex, REALTYPE freq);
// obsolete, use pci416_set_pllreg(brdindex, valA, valN) instead
///////////////////////////////////////////////////////////////////////////////
// writes current software value of the pll data register to hardware
//
///////////////////////////////////////////////////////////////////////////////
//void LIB_TYPE update_pt_pll_reg(DWORD brdindex);
// obsolete, use pci416_set_timer(DWORD brdindex, WORD mode,
// WORD counter02, WORD counter1);
///////////////////////////////////////////////////////////////////////////////
// Updates 8254 counter register hardware with current software values
//
///////////////////////////////////////////////////////////////////////////////
//UINT LIB_TYPE set_pt_timer(DWORD brdindex, UINT n,UINT mode,UINT count);
///////////////////////////////////////////////////////////////////////////////
// Set A/D Clock Rate
// Automatically sets best possible clock source mux and counter values
///////////////////////////////////////////////////////////////////////////////
int LIB_TYPE set_pt_ad_clock_rate(DWORD brdindex,REALTYPE fs);
///////////////////////////////////////////////////////////////////////////////
// Set A/D Trigger Rate (timers 0,1)
// Returns (1) if error
// *actual = real trigger rate produced by hardware
///////////////////////////////////////////////////////////////////////////////
UINT LIB_TYPE set_pt_trigger_rate(DWORD brdindex,REALTYPE ft,
REALTYPE *actual);
///////////////////////////////////////////////////////////////////////////////
// set trigger mode to internal
// generate single trigger
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE generate_single_internal_trigger(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE reset_pt_trigger_timer(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Sets DAC hardware voltage
// assumes +-10V range
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_dac_voltage(DWORD brdindex, REALTYPE volts);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Sets DAC hardware code
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_pt_dac_code(DWORD brdindex, UINT code);
///////////////////////////////////////////////////////////////////////////////
// Reads 1 DWORDSfrom pt a/d sample storage fifo
// returns 0xffffffff in case of error
// get error code with pci416_getError
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_pt_fifo(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Read Multiple DWORDS from pt a/d sample storage fifo
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE read_pt_fifo_many(DWORD brdindex, LPDWORD buffer, DWORD count);
/*****************************************************************************
BRIDGE FUNCTIONS (S593X)
*****************************************************************************/
///////////////////////////////////////////////////////////////////////////////
// Resets the (Inbound) DMA FIFO on the S593x controller
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE reset_dma_fifo( DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Sets the (Inbound) DMA fif0 bus master mode 1-> request on HF
// 0-> reqest on not(EF)
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_dma_request_half_full( DWORD brdindex,UINT md);
///////////////////////////////////////////////////////////////////////////////
// Enable/Disable (T/F) Bus master write transfers from FIFO
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE enable_dma_transfers( DWORD brdindex,UINT enable);
///////////////////////////////////////////////////////////////////////////////
// Updates the value of the S593x Bus Master Control/Status Reg
///////////////////////////////////////////////////////////////////////////////
/* obsolete
// use pci416_set_mcsrreg(brdindex, mode, data, &retdata);
void LIB_TYPE update_mcr(DWORD brdindex);
*/
///////////////////////////////////////////////////////////////////////////////
// Updates the DMA(Write) transfer count register (count is in bytes)
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_dma_transfer_count(DWORD brdindex, DWORD bytes);
///////////////////////////////////////////////////////////////////////////////
// Reads the DMA(Write) transfer count register (count is in bytes)
// returns 0xffffffff in case of error
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_dma_transfer_count(DWORD brdindex);
/* obsolete use pci416_setup_dma and pci416_reload_dma
///////////////////////////////////////////////////////////////////////////////
// Updates the DMA(Write) transfer Address register
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_dma_dest_address(DWORD brdindex, DWORD dest);
///////////////////////////////////////////////////////////////////////////////
// Reads the DMA(Write) destination addr. register
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_dma_dest_address(DWORD brdindex);
*/
// not implemented DMA does not work on interrupt
///////////////////////////////////////////////////////////////////////////////
// Enables/Disables S593x interrupt to HOST on dma completion (Write Transfer)
///////////////////////////////////////////////////////////////////////////////
//void LIB_TYPE enable_dma_interrupt(DWORD brdindex, UINT enable);
/*
obsolete use pci416_dma_status
///////////////////////////////////////////////////////////////////////////////
// Returns the Write(dma) transfer complete status bit (1=complete)
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_dma_transfer_status(DWORD brdindex);
*/
/* not implemented DMA does not work on interrupt
///////////////////////////////////////////////////////////////////////////////
// Resets the MAILBOX INTERRUPT status bit (INTERRUPT) (write1 clRs)
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE reset_dma_transfer_status(DWORD brdindex);
*/
/* obsolete, use pci416_read_intcsr,
pci416_set_intcsr,
pci416_read_mcsr,
pci416_set_mcsr,
read_pci_port,
write_pci_port
///////////////////////////////////////////////////////////////////////////////
// Returns 1 is the the DMA(Write X-fer) FIFO on the AMCC controller is FULL
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE dma_fifo_full(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Returns 1 is the the DMA(Write X-fer) FIFO on the AMCC controller is empty
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE dma_fifo_empty(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Reads the DMA(Write X-fer) FIFO on the AMCC controller
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_dma_fifo_reg(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Updates the value of the S593x Interrupt Control/Status Reg
///////////////////////////////////////////////////////////////////////////////
void LIB_TYPE update_intcsr(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Reads the value of the S593x Interrupt Control/Status Reg
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_intcsr(DWORD brdindex);
///////////////////////////////////////////////////////////////////////////////
// Reads the value of the S593x Master Control/Status Reg
///////////////////////////////////////////////////////////////////////////////
DWORD LIB_TYPE read_msr(DWORD brdindex);
// functions not needed for board operation in Windows
// used for internal test only -> see DOS code p416_set
//////////////////////////////////////////////////////////////////////////////
// Checks if NVRAM access pins are busy
// returns non-zero if busy
//////////////////////////////////////////////////////////////////////////////
UINT LIB_TYPE nvram_busy(DWORD brdindex);
/////////////////////////////////////////////////////////////////////////////
// Set EPROM address for Data access
//////////////////////////////////////////////////////////////////////////////
void LIB_TYPE set_nvram_address(DWORD brdindex, UINT addr);
//////////////////////////////////////////////////////////////////////////////
// Read byte from NVRAM
//////////////////////////////////////////////////////////////////////////////
unsigned char LIB_TYPE read_nvram( DWORD brdindex, UINT addr);
//////////////////////////////////////////////////////////////////////////////
// Write byte to NVRAM
//////////////////////////////////////////////////////////////////////////////
void LIB_TYPE write_nvram( DWORD brdindex, UINT addr,
unsigned char data);
*/
#endif

View File

@ -0,0 +1,171 @@
///////////////////////////////////////////////////////////////////////////////
//
// pcilib32.h: defines data structures to access Datel PCI devices
// Copyright (c) Datel, Inc. 1997
// Platform: Win32, Win95
// Compiler: MVC4.0 + DDK
// Version: 3.0
// Author: GS
// created: 2/4/97
// modified: 7/10/98
// History: V1.1 MCSR and INTCSR moved from "pci416df.h"
// V3.0 new defines for MWAR and MTC Reg
///////////////////////////////////////////////////////////////////////////////
#ifndef PCILIB32_H
#define PCILIB32_H
#if defined (__cplusplus)
extern "C"
{
#endif
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifndef DWORD
#define DWORD unsigned long
#define WORD unsigned short
#define BYTE unsigned char
#endif
#define HIGH_BYTE(ax) (ax>>8)
#define LOW_BYTE(ax) (ax & 0xff)
#define HIGH_WORD(ax) (ax>>16)
#define LOW_WORD(ax) (ax & 0xffff)
#ifndef LPDWORD
#define LPDWORD unsigned long far *
#endif
#define PCI_CONFIG_DATA_OPORT 0xCF8
#define PCI_CONFIG_DATA_IPORT 0xCFC
// Address offsets for PCI bus opertion registers
#define PCI_OPREG_FIFO 0x20
#define PCI_OPREG_MWAR 0x24
#define PCI_OPREG_MWTC 0x28
#define PCI_OPREG_MRAR 0x2C
#define PCI_OPREG_MRTC 0x30
#define PCI_OPREG_INTCSR 0x38
#define PCI_OPREG_MCSR 0x3C
#define PCI_OPREG_IMB4 0x1C
#define BITS unsigned int
struct STRUCT_ADDRESS{
unsigned long loc;
unsigned int type; //TRUE = IO, FALSE = MEM
};
typedef struct STRUCT_ADDRESS TYPE_ADDRESS;
// PCI configuration address register fields
typedef struct
{
BITS Zero : 2; // must be set to zero
BITS RegisterNo : 7;
BITS FunctionNo : 3;
BITS DeviceNo : 5;
BITS BusNo : 7;
BITS Reserved : 7;
BITS MSB : 1; // must be set to 1
}PCI_CONF_ADDR_REGISTER_FIELDS;
typedef union
{
PCI_CONF_ADDR_REGISTER_FIELDS fields;
DWORD all;
} PCI_CONF_ADDR_REGISTER;
// PCI Configuration Space
typedef struct {
DWORD dev_ven_id; // = WORD vendor_id + WORD device_id
DWORD stat_cmd_rgs; //= WORD command +WORD status;
DWORD class_code_revision_id; // = BYTE revision_id + BYTE class_code[3]
DWORD bist_head_latt_cach; // =BYTE cache_line_size+ BYTE latency_timer+
// BYTE header_type+ BYTE bist;
DWORD base_address[6];
DWORD reserved1[2];
DWORD expansion_rom_base;
DWORD reserved2[2];
DWORD mlat_mgrant_irqp_irql; // BYTE interrupt_line+ BYTE interrupt_pin+
// BYTE min_grant + BYTE max_latency;
// BYTE device[196]; not used on AMCC S5933 PCI controller
}PCI_TYPE_CONFIG;
//Readout structure for PCI configuration Space Header
typedef union {
PCI_TYPE_CONFIG regs;
DWORD buffer[16];
} PCI_CONFIG_SPACE;
typedef PCI_CONFIG_SPACE *PCI_CONFIGS_PTR;
struct BRIDGE_INTCSR{
BITS out_mail_box_interrupt_select : 4; // 0:3
BITS enable_out_mail_box_interrupt : 1; // 4
BITS reserved1 : 3; // 5:7
BITS in_mail_box_interrupt_select : 4; // 8:11
BITS enable_in_mail_box_interrupt : 1; // 12
BITS reserved2 : 1; // 13
BITS dma_complete_interrupt_enable : 1; // 14
BITS enable_read_complete_interrupt: 1; // 15
BITS out_mail_box_interrupt_status : 1; // 16
BITS in_mail_box_interrupt_status : 1; // 17
BITS dma_transfer_status : 1; // 18
BITS read_transfer_status : 1; // 19
BITS master_abort_status : 1; // 20
BITS target_abort_status : 1; // 21
BITS reserved3 : 1; // 22
BITS interrupt_status : 1; // 23
BITS fifo_endian_control : 8; // 24:31
};
typedef union{
struct BRIDGE_INTCSR fields;
DWORD buffer;
} TBRIDGE_INTCSR_REGISTER;
struct BRIDGE_MCSR{
BITS out_fifo_status : 3; // 0:2
BITS fifo_full : 1; // 3 (inbound fifo)
BITS fifo_half_full : 1; // 4 .
BITS fifo_empty : 1; // 5 .
BITS out_count_zero : 1; // 6
BITS dma_count_zero : 1; // 7 (incomming count)
BITS read_write_priority : 1; // 8
BITS dma_request_condition : 1; // 9 (write fifo)
BITS dma_enable : 1; // 10 (write transfer enable)
BITS reserved1 : 1; // 11
BITS not_needed_by_416 : 4; // 12:15
BITS eprom_data : 8; // 16:23
BITS add_on_reset : 1; // 24
BITS out_fifo_reset : 1; // 25 //used for DMA_ENABLE 22V10
BITS fifo_reset : 1; // 26 (inbound fifo)
BITS mail_box_reset : 1; // 27
BITS reserved2 : 1; // 28
BITS eprom_control : 3; // 29:31
};
typedef union{
struct BRIDGE_MCSR fields;
DWORD buffer;
} TBRIDGE_MCSR_REGISTER;
typedef struct {
DWORD buffer;
} TBRIDGE_MWAR_REGISTER;
typedef struct {
DWORD buffer;
} TBRIDGE_MWTC_REGISTER;
#if defined (__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////
//
// timer.h: 82C54 command register bits
// and mode definition used by the PCI 416-board
// Copyright (c) Datel, Inc. 1997
// Version: 1.0
// Author: GS
// created: 2/4/97
// modified: 4/15/96
///////////////////////////////////////////////////////////////////////////////
#ifndef TIMER_H
#define TIMER_H
#define SEL0 (unsigned int) 0x00 // select counter # 0
#define SEL1 (unsigned int) 0x40 // select counter # 1
#define SEL2 (unsigned int) 0x80 // select counter # 2
#define LSBMSB (unsigned int) 0x30 // load lsb followed by msb
#define MODE0 (unsigned int) 0x00 // modes
#define MODE1 (unsigned int) 0x02
// 8254 Constants
#define MODE2 (unsigned int) 0x04 // software trigger
#define MODE3 (unsigned int) 0x06
#define MODE4 (unsigned int) 0x08 // hardware trigger
#define MODE5 (unsigned int) 0x0A
#define BINARY (unsigned int) 0x00 // binary count
#define BCD (unsigned int) 0x01 // bcd count
#define READBACK (unsigned int) 0xC0 // read back counter command
#define CLOCK1 8000000L // clock frequency for Sample rate counter (1)
#define CLOCK2 500000L // clock frequency for internal trigger rate(2)
enum{TM_SINGLE_TRIGGER, TM_CONT_TRIGGER, TM_RESET_TRIGGER, TM_ADCLOCK};
#endif

View File

@ -0,0 +1,33 @@
/*****************************************************************************
*
* ADMSTATS.H -- Header File A/D Module Parameters
* Copyright 1997 Datel Corporation. All Rights Reserved.
* Version 3.1.
* Author: GS
* created : 1/24/97
* modified: 10/5/98
*****************************************************************************/
#ifndef ADMSTATS_H
#define ADMSTATS_H
#include "pci416df.h"
const ADM_STATS adm_stats[]={
//mod,bits, chs, single, scan,chrg,inc,scne,scnt,ssh,scyc,mnV,mxV,mnc,mxc)
{'A', 12, 4, 1.0e6F, 250e3F, 0, 1, 1, 3, 1, 1, 0.0F,0.0F,0,0}, // A
{'B', 14, 4, 500e3F, 333e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // B
{'C', 12, 4, 1.6e6F, 500e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // C
{'D', 12, 1, 5e6F, 5e6F, 0, 0, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // D
{'E', 12, 16, 2.0e6F, 500e3F, 0, 1, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // E
{'F', 12, 2, 2.0e6F, 2.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // F
{'G', 14, 2, 1.0e6F, 1.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // G
{'H', 12, 1, 10e6F, 10e6F, 0, 0, 0, 0, 0, 0, 0.0F,0.0F,0,0}, // H
{'J', 12, 8, 400e3F, 250e3F, 15, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // J
{'K', 12, 2, 5.0e6F, 5.0e6F, 2, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // K
{'L', 12, 16, 400e3F, 190e3F, 15, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // L
{'M', 16, 4, 200e3F, 200e3F, 8, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // M
{'N', 14, 2, 5e6F, 5e6F, 2, 0, 0, 0, 1, 1, 0.0F,0.0F,0,0}, // N
{'P', 14, 4, 3e6F, 3e6F, 8, 0, 0, 0, 1, 0, 0.0F,0.0F,0,0}, // P
};
#define MAX_ADMNO (sizeof(adm_stats)/sizeof(ADM_STATS))
#endif

View File

@ -0,0 +1,206 @@
///////////////////////////////////////////////////////////////////////////////
//
// pci416df.h: defines data structures to access PCI416 DAQ devices
// Copyright (c) Datel, Inc. 1997
// Platform: Win95, Win NT
// Compiler: MVC4.0 + DDK
// Version: 3.0
// Author: GS
// created: 2/4/97
// modified: 7/9/98
// History: V1.1 MCSR and INTCSR moved to "pcilib32.h"
// V3.0 now for Win 95 and NT
///////////////////////////////////////////////////////////////////////////////
#ifndef PCI416DF_H
#define PCI416DF_H
#include "pcilib32.h"
#ifndef REALTYPE
#define REALTYPE double
#endif
#define UROUND(X) (DWORD)(X + 0.5)
#define FFIFO 0x2000 // samples = WORDS
#define HFIFO 0x1000 // samples
#define CAPS_SIZE 5 // number of ULONGS for Caps buffer
// PASS THRU ADDRESS REGISTER LATCH
#define ARL_CMD_STS_REG 0l
#define ARL_SMPLCNT_FIFORD 4l
#define ARL_CHADDRREG_CLEARFIFO 8l
#define ARL_AD_ENABLE 12l
#define ARL_PLLREG 16l
#define ARL_CNTR0 0l
#define ARL_CNTR1 4l
#define ARL_CNTR2 8l
#define ARL_CNT_CTRL_REG 12l
#define ARL_PORTA 16l
#define ARL_PORTB 20l
#define ARL_PORTC 24l
#define ARL_PORT_CTRL_REG 28l
#define ARL_DAC_REG 32l
/* Bit masks for register write operations*/
#define RWF_OVR 0x00 // reg = newval ONLY
#define RWF_OR 0x01 // reg |= newval ONE OPTION
#define RWF_AND 0x02 // reg &= newval VALID AT THE TIME
#define RWF_RB 0x80 // read back register value if register is r/w
// can be set with one of the above
/* DMA mode flags */
#define DMA_SINGLE 0
#define DMA_DOUBLE 1
struct PCI416_COMMAND_REGISTER{
BITS trigger_select : 2;
BITS interrupt_select : 1;
BITS scan_select : 1;
BITS auto_increment : 1;
BITS marker_select : 1;
BITS pretrigger : 1;
BITS interrupt_enable : 1;
BITS pll : 3;
BITS mux_select : 3;
BITS adm_control : 2;
BITS dma_on : 1;
BITS spare : 15;
};
typedef union{
struct PCI416_COMMAND_REGISTER fields;
DWORD buffer;
} TPCI416_COMMAND_REGISTER;
struct PCI416_STATUS_REGISTER{
BITS trigger_select : 2;
BITS scan_select : 1;
BITS auto_increment : 1;
BITS acquire : 1;
BITS marker_select : 1;
BITS pretrigger : 1;
BITS analog_trigger : 1;
BITS model : 4;
BITS arm : 1;
BITS empty : 1;
BITS half_full : 1;
BITS full : 1;
BITS spare : 16;
};
typedef union{
struct PCI416_STATUS_REGISTER fields;
DWORD buffer;
} TPCI416_STATUS_REGISTER;
struct PCI416_SAMPLE_COUNT_REGISTER{
unsigned long count;
};
struct PCI416_CHANNEL_REGISTER{
BITS channel : 4;
BITS scan : 4;
BITS led : 1;
BITS spare1 : 7;
BITS spare2 : 16;
};
typedef union{
struct PCI416_CHANNEL_REGISTER fields;
DWORD buffer;
} TPCI416_CHANNEL_REGISTER;
struct PCI416_CONVERT_ENABLE_REGISTER{
BITS sparel : 15;
BITS convert : 1;
BITS spareu : 16;
};
typedef union{
struct PCI416_CONVERT_ENABLE_REGISTER fields;
DWORD buffer;
} TPCI416_CONVERT_ENABLE_REGISTER;
struct PCI416_PLL_REGISTER{
BITS data : 4;
BITS spare1 : 12;
BITS spare2 : 16;
};
typedef union{
struct PCI416_PLL_REGISTER fields;
DWORD buffer;
} TPCI416_PLL_REGISTER;
struct PCI416_DAC_REGISTER {
BITS data : 12;
BITS spare1 : 4;
BITS spare2 : 16;
};
typedef union{
struct PCI416_DAC_REGISTER fields;
DWORD buffer;
} TPCI416_DAC_REGISTER;
typedef struct {
union{
DWORD both;
struct {
int low;
int high;
} word;
}sample;
} TYPE_FIFO_DATA;
typedef struct{
WORD model;
WORD bits;
WORD channels;
float fmax_single;
float fmax_scan;
WORD channel_scan;
WORD autoincr_scan;
WORD scan_select;
WORD scan_count;
WORD ssh;
WORD scycle;
float minv;
float maxv;
long int mincode,maxcode;
}ADM_STATS;
typedef struct BoardInfo
{
DWORD ADMindex,
BusNo,
SlotNo,
VendorID,
DeviceID,
CommandR,
StatusR,
ClassCodeR,
IRLR,
BADR[6];
} TBoardInfo;
typedef TBoardInfo *TBoardInfoPtr;
enum{single_trigger,continuous_trigger};
enum{T_INTERNAL,T_EXTERNAL,T_ANALOG,T_ANALOG_FE}; //trigger source
enum{M_DMA, M_HF_POLL, M_HF_POLL_DDISK,M_HF_INT} ; //acq_mode
enum{D_MEM, D_DIRECT}; // disk_mode: data to memory or direct to disk
enum{F_BINARY};
enum{C_INTERNAL,C_EXTERNAL}; //clock_source
enum{R_NORMAL,R_BATCH,R_START_EXIT}; //run_mode
enum{I_DMA, I_HF, I_EOS }; // interrupt mode
enum{B_NORMAL, B_MARKER}; //marker mode
#endif

View File

@ -0,0 +1,233 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "core/core.h"
#include "Datel-PCI416.h"
DatelPCI416::DatelPCI416(const ttlout& t_line){
trigger_line=t_line;
PCI_DLL = LoadLibrary("PCI41632");
if (PCI_DLL==NULL) {
throw ADC_exception("found no library\n");
}
/* import all functions from dll and exit on error*/
# define pci41632dll_getproc(name) \
name = (TFP_ ## name)GetProcAddress(PCI_DLL,#name); \
if (name == NULL) {FreeLibrary(PCI_DLL); throw ADC_exception("found no " #name " function in DLL");}
# include "PCI416_func_import.cpp"
if (pci416_init(&brdcount)!=NOERROR) {
brdcount=0;
throw ADC_exception("initialisation failed");
}
// now hardcoded, use first board
board=0;
if (NOERROR!=pci416_getcaps(board,4,caps))
throw ADC_exception("getcaps failed");
fprintf(stderr,"Datel PCI416 caps: FIFO size: %d, DMA size: %d, index ADM %d, acqmode %d\n",caps[0],caps[1],caps[2],caps[3]);
sample_count=0;
trigger_count=0;
sample_frequency=0;
trigger_line.ttls=1<<2;
trigger_line.id=0;
}
DatelPCI416::~DatelPCI416() {
pci416_close(board);
FreeLibrary(PCI_DLL);
}
/**
here the data aquisition unit is configured and adds the necessary pusle components to the program
*/
void DatelPCI416::set_daq(state& exp) {
sample_count=0;
trigger_count=0;
sample_frequency=0;
// todo: cleanup before exception...
state_iterator si(dynamic_cast<state_sequent&>(exp));
state* a_state=(state*)si.get_state();
/* loop over all states */
while (NULL!=a_state) {
// collect analogin sections in state
std::list<analogin*> inputs;
/* loop over all device definitions in a state */
state::iterator i=a_state->begin();
while (i!=a_state->end()) {
analogin* input=dynamic_cast<analogin*>(*i);
if (input!=NULL) {
/* collect appropiate analogin sections, forget others */
if (input->samples<=0 || input->sample_frequency<=0)
delete input;
else
inputs.push_back(input);
i=a_state->erase(i);
}
else
++i;
}
if (!inputs.empty()) {
/* evaluate the found analogin definitions */
if (inputs.size()>1) {
while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();}
throw ADC_exception("can not handle more than one analogin section per state");
}
if (trigger_count==0) {
sample_count=inputs.front()->samples;
sample_frequency=inputs.front()->sample_frequency;
trigger_count=si.get_count();
}
else if ( sample_count==inputs.front()->samples && sample_frequency==inputs.front()->sample_frequency ) {
trigger_count+=si.get_count();
}
else {
while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();}
throw ADC_exception("Sorry, but multittriggering requires same parameters in all analogin sections");
}
if (a_state->length<1.0*sample_count/sample_frequency) {
fprintf(stderr,"WARINING: state is shorter than aquisition time\n");
}
a_state->push_back(trigger_line.copy_new());
// to do: more analogin sections per state
while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();}
}
// send a trigger pulse
// todo only short trigger pulse
a_state=(state*)si.next_state();
}
/* nothing to do! */
if (trigger_count==0 || sample_count==0) return;
if (sample_count*trigger_count*4>caps[1])
throw ADC_exception("required dma buffer size is too big for pci slot config");
fprintf(stderr,"samples: %d, triggers: %d, frequency=%f\n", sample_count, trigger_count, sample_frequency);
/* initialise board */
if (NOERROR!=set_modes(board,
0, // clock source: 0=internal
sample_frequency, // sample rate
sample_count*2, // samples per trigger
1, // trigger src: 0=internal, 1=external digital
1, // channel (1= Ch1 and Ch2)
0, // pretrigger
1, // scan (multichannel=1)
0 // marker
))
throw ADC_exception("set_modes failed...");
// clear fifo, even when using dma
if (NOERROR!=pci416_clear_fifo(board)) throw ADC_exception("clear_fifo failed");
DWORD dma_buffer_size[2];
dma_buffer_size[0]=dma_buffer_size[1]=sample_count*trigger_count*4; //bytes
dma_buffer_handle=0;
if (NOERROR!=pci416_setup_dma(board,DMA_SINGLE,dma_buffer_size,&dma_buffer_handle))
throw ADC_exception("setup_dma failed");
// start single aquisition
double freq=1.0;
// start_daq(board, trigger_mode, frequency) trigger mode: 0 external
if (NOERROR!=start_daq(board,0,&freq)) throw ADC_exception("start_daq failed");
}
adc_result* DatelPCI416::get_samples(double timeout) {
// nothing to do here
if (trigger_count==0 || sample_count==0) return new adc_result(1,0,NULL);
const size_t poll_time=10000;
double time_spent=0;
// busy wait for end
fprintf(stderr,"waiting for ADC");
while (core::term_signal==0) {
DWORD dma_status=0;
// status NOT DONE=0, Done=1
if (pci416_dma_status(board,&dma_status)!=NOERROR)
throw ADC_exception("dma_status failed");
if (dma_status!=0) break;
usleep(poll_time);
time_spent+=1e-6*poll_time;
fprintf(stderr,".",dma_status);
fflush(stderr);
if (time_spent>timeout) {
fprintf(stderr," timeout!\n");
throw ADC_exception("ran into timeout!");
}
}
fprintf(stderr," done\n");
if (core::term_signal!=0) return NULL;
if (NOERROR!=stop_daq(board))
throw ADC_exception("stop daq failed");
if(NOERROR!=pci416_stop_dma(board,NULL))
throw ADC_exception("stop_dma failed");
// copy results...
DWORD resulting_bytes=sample_count*trigger_count*4; // bytes
short int* data=(short int*)malloc(resulting_bytes);
if (data==NULL) throw ADC_exception("Could not allocate enough memory");
if (NOERROR!=pci416_copy_dmabuffer(board, DMA_SINGLE, 0, &resulting_bytes, (DWORD*)data)) {
free(data);
throw ADC_exception("copy_dmabuffer failed");
}
#if 0
fprintf(stderr,"got %d bytes back\n",resulting_bytes);
FILE* binfile=fopen("/tmp/bla.bin","w+");
if (binfile!=NULL) {
fwrite(data,1,resulting_bytes, binfile);
fclose(binfile);
}
else
fprintf(stderr,"could not open file\n");
#endif
/* return them */
return new adc_result(1, sample_count*trigger_count, data, sample_frequency);
}
void DatelPCI416::sample_after_external_trigger(const double rate, const size_t samples, double sensitivity, size_t resolution) {
const int board=0;
// try dma
if (NOERROR!=set_modes(board,
0, // clock source: 0=internal
rate, // sample rate
samples*2, // samples per trigger
1, // trigger src: 0=internal, external digital =1
1, // channel (1= Ch1 and Ch2)
0, // pretrigger
1, // scan
0 // marker
))
throw ADC_exception("set_modes failed...");
// clear fifo, even when using dma
if (NOERROR!=pci416_clear_fifo(board)) throw ADC_exception("clear_fifo failed");
DWORD dma_buffer_size[2];
dma_buffer_size[0]=dma_buffer_size[1]=samples*trigger_count*4; //bytes
DWORD dma_buffer_handle=0;
if (NOERROR!=pci416_setup_dma(board,DMA_SINGLE,dma_buffer_size,&dma_buffer_handle))
throw ADC_exception("setup_dma failed");
fprintf(stderr,"buffersize %d\n",dma_buffer_size[0]);
// start single aquisition
double freq=1.0;
if (NOERROR!=start_daq(board,0,&freq)) throw ADC_exception("start_daq failed");
}

View File

@ -0,0 +1,97 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef DATELPCI416
#define DATELPCI416
#include "drivers/ADC.h"
#include "core/states.h"
#include <cstdlib>
#include <windows.h>
#include "416inc/pcilib32.h"
#include "416inc/pci416df.h"
/**
\defgroup datelpci416 Datel PCI416 adc card
\ingroup drivers
\brief supports Datel PCI416 pci cards with dma data transfer
@{
*/
class DatelPCI416: public ADC {
HINSTANCE PCI_DLL;
// make definitions private
# include "416inc/41632DLL.H"
/** the board this driver works on */
long unsigned int board;
/** number of available boards */
long unsigned int brdcount;
/**
\brief the capabilities of the PCI slot used by this card
the members of this structure are:
0 sizeFIFO;
1 bufsizeDMA;
2 indexADM;
3 acqmode;
*/
DWORD caps[4];
/**
the sample count per trigger pulse and per channel
*/
size_t sample_count;
/**
the count of expected triggers
*/
size_t trigger_count;
/**
sampling frequency of initialised card
*/
double sample_frequency;
/**
when initialised, this handle gives access to data
*/
DWORD dma_buffer_handle;
/**
the ttl signal for pulse programmer
*/
ttlout trigger_line;
public:
DatelPCI416(const ttlout& t_line);
~DatelPCI416();
/**
start sampling after trigger and return field of int
*/
virtual void sample_after_external_trigger(const double rate, const size_t samples, double sensitivity=5.0, size_t resolution=12);
/**
here the data aquisition unit is configured and adds the necessary pusle components to the program
\exception ADC_exception if data aquisition can not be done by this driver, reason is given
*/
virtual void set_daq(state& exp);
/**
read the sample data
*/
virtual adc_result* get_samples(double timeout=0.0);
};
/**
@}
*/
#endif

View File

@ -0,0 +1,32 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
# für den ersten Anlauf
CPP=g++
CPPFLAGS=-g -I. -I../..
LDFLAGS=
.PHONY: all clean install
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
all: Datel-PCI416.o
Datel-PCI416.o: Datel-PCI416.cpp Datel-PCI416.h PCI416_func_import.cpp
$(CPP) -c $(CPPFLAGS) $< -o $@
PCI416_func_import.cpp: 416inc/41632DLL.H
grep "^TFP_" $< |sed 's/.* \(.*\);/pci41632dll_getproc(\1)/' >$@
clean:
for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \
rm -f *.o *~ *.stackdump PCI416_func_import.cpp
install:

View File

@ -0,0 +1,302 @@
/* **************************************************************************
Author: Holger Stork, Achim Gaedke
Created: January 2005
****************************************************************************/
#include "Eurotherm-2000Series.h"
#include <cstdio>
#include <cmath>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <locale.h>
#include <string>
Eurotherm2000Series::Eurotherm2000Series(const std::string& dev_name, int dev_address, int failure_status_mask): tempcont() {
/*
initialization of serial device
*/
device_name=dev_name;
failure_mask=failure_status_mask;
serial_dev=open(dev_name.c_str(),O_RDWR|O_NONBLOCK);
if (serial_dev<0)
throw Eurotherm2000Series_error("could not open serial device");
// set attributes now
termios the_attributes;
the_attributes.c_iflag=0;
the_attributes.c_oflag=0;
// 7 bit per byte, even parity, reciever enabled
the_attributes.c_cflag=CS7|PARENB|CREAD;
the_attributes.c_lflag=0;
// baud rate
cfsetospeed(&the_attributes,B19200);
cfsetispeed(&the_attributes,B19200);
if(tcsetattr(serial_dev,TCSANOW,&the_attributes)!=0)
throw Eurotherm2000Series_error("could not configure serial line");
address=dev_address;
(void)setlocale(LC_NUMERIC,"C");
fp_format="%05.1f";
int_format="%05d.";
hex_format=">%04x";
std::map<std::string,std::string> config;
// configure for <20>K output
config["Q1"]="2.";
// configure for nnn.n format output/precision
config["QD"]="1.";
configure(config);
set_history_stepsize(1);
device_failed=0;
}
void Eurotherm2000Series::configure(const std::map<std::string,std::string>& config) {
std::string error_message;
try {
std::string mode;
const char config_mode[]="0002.";
read_value("IM",mode);
for (std::map<std::string,std::string>::const_iterator i=config.begin();i!=config.end();++i) {
try {
std::string value;
read_value(i->first, value);
fprintf(stderr, "%s: %s (%s)", i->first.c_str(), value.c_str(), i->second.c_str());
if (value!=i->second) {
if (mode!=config_mode) set_value("IM",config_mode);
set_value(i->first,i->second);
mode=config_mode;
}
}
catch (Eurotherm2000Series_error e){error_message+=i->first+"->"+i->second+", ";}
}
if (mode==config_mode) {
set_value("IM","0000.");
// read superficial zero byte
char buffer[1];
while (read(serial_dev,buffer,1)<=0) sleep(1);
// wait for restarted communication
int timeout=10;
while (1) {
if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after configuring");
try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout;sleep(1); continue;}
break;
}
}
}
catch (const Eurotherm2000Series_error& e) {
throw Eurotherm2000Series_error("got error while configuration: "+std::string(e.what()));
}
if (error_message.size()!=0) {
throw Eurotherm2000Series_error("Could not set "+error_message.substr(0,error_message.size()-1));
}
}
void Eurotherm2000Series::reset() {
set_value("IM","0000.");
// read superficial zero byte
char buffer[1];
while (read(serial_dev,buffer,1)<=0) sleep(1);
// wait for restarted communication
int timeout=10;
while (1) {
if (timeout<0) throw Eurotherm2000Series_error("could not establish connection after reset");
try {get_summary_status();} catch(Eurotherm2000Series_error){--timeout; sleep(1); continue;}
break;
}
}
Eurotherm2000Series::~Eurotherm2000Series() {
/**
close serial device, clean up
*/
close(serial_dev);
}
void Eurotherm2000Series::read_value(const std::string& param_name, std::string& return_value) const{
// assemble message without channel
std::string message;
message+=4;
message+='0'+(address/10);
message+='0'+(address/10);
message+='0'+(address%10);
message+='0'+(address%10);
message+=param_name;
message+=5;
// write message to device
pthread_mutex_lock((pthread_mutex_t*)&device_lock);
write(serial_dev,message.c_str(),message.size());
// read answer
unsigned char got_byte=0;
return_value.clear();
// sleep minimum latency time for parameter reading
timespec read_latency;
read_latency.tv_sec=0; read_latency.tv_nsec=2000000;
nanosleep(&read_latency,NULL);
// now use shorter steps
int timeout=100;
read_latency.tv_nsec=1000000;
while (return_value.size()<2 || return_value[return_value.size()-2]!=3) {
int result=read(serial_dev,&got_byte,1);
if (result<=0) {
if (result<0 && errno!=EAGAIN) {
return_value.clear();
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
throw Eurotherm2000Series_error("read_value: error while reading");
}
if (timeout<0) {
return_value.clear();
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
throw Eurotherm2000Series_error("read_value: timeout while reading");
}
// go on sleeping for a while
nanosleep(&read_latency,NULL);
timeout--;
continue;
}
if (return_value.size()==0 && got_byte==4) throw Eurotherm2000Series_error("read_value: invalid register");
return_value+=got_byte;
}
pthread_mutex_unlock((pthread_mutex_t*)&device_lock);
if (0) {
for (std::string::const_iterator i=return_value.begin();i!=return_value.end();++i)
fprintf(stderr,"%02x",*i);
fprintf(stderr,"\n");
}
// test bcc
unsigned char bcc=return_value[1];
for (size_t i=2;i<return_value.size()-1;++i) bcc^=return_value[i];
if (bcc!=got_byte) {
return_value.clear();
throw Eurotherm2000Series_error("read_value: bcc missmatch");
}
// compare C1 and C2
if (return_value.substr(1,param_name.size())!=param_name) {
return_value.clear();
throw Eurotherm2000Series_error("read_value: register name echo mismatch");
}
// cut protocol bytes
return_value.erase(0,3);
return_value.resize(return_value.size()-2);
return;
}
int Eurotherm2000Series::set_value(const std::string& param_name, const std::string& value) {
// comunicate with device
std::string message;
message+=4; //EOT
message+='0'+(address/10);
message+='0'+(address/10);
message+='0'+(address%10);
message+='0'+(address%10);
message+=2; //STX
message+=param_name.substr(0,2);
message+=value;
message+=3; //ETX
// calculate bcc byte
unsigned char bcc=message[6];
for (size_t i=7;i<message.size();++i) bcc^=message[i];
message+=(unsigned char)bcc; //BCC
#if 0
printf("param: %s, value: %s: ",param_name.c_str(),value.c_str());
for (size_t i=0; i<message.size();i++)
printf("%02x ",(unsigned char)message[i]);
printf("\n");
#endif
pthread_mutex_lock(&device_lock);
write(serial_dev,message.c_str(),message.size());
// sleep minimum latency time for value setting
timespec write_latency;
write_latency.tv_sec=0; write_latency.tv_nsec=5000000;
nanosleep(&write_latency,NULL);
// wait shorter while polling
write_latency.tv_nsec=1000000;
int timeout=1000;
unsigned char got_byte;
int result;
while ((result=read(serial_dev,&got_byte,1))<=0) {
if (result<0 && errno!=EAGAIN) {
pthread_mutex_unlock(&device_lock);
throw Eurotherm2000Series_error("set_value: read error");
}
if (timeout<0) {
pthread_mutex_unlock(&device_lock);
throw Eurotherm2000Series_error("set_value: timeout occurred");
}
nanosleep(&write_latency,NULL);
timeout--;
}
pthread_mutex_unlock(&device_lock);
if (got_byte==0x15) {
char message_buffer[256];
std::string ee_answer;
read_value("EE",ee_answer);
snprintf(message_buffer, 256, "set_value: negative acknoledge (ee=0x%02x)",ee_answer.c_str()[0]);
throw Eurotherm2000Series_error(message_buffer);
}
if (got_byte!=6) {
char message_buffer[256];
snprintf(message_buffer,256,"(0x%02x)",got_byte);
throw Eurotherm2000Series_error(std::string("set_value: no acknoledge ")+message_buffer);
}
return 0;
}
double Eurotherm2000Series::get_temperature() const {
int summary_status=get_summary_status();
if (failure_mask&summary_status) {
char message_buffer[10];
snprintf(message_buffer,10,"0x%04x",summary_status);
throw Eurotherm2000Series_error(std::string("sensor/heater/temperature range fault: ")+message_buffer);
}
std::string answer;
read_value("PV",answer);
return strtod(answer.c_str(),NULL);
}
double Eurotherm2000Series::set_setpoint(double ct) {
char buffer[8];
snprintf(buffer,8,fp_format.c_str(),ct);
set_value("SL",buffer);
// extra wait, until the get_setpoint function returns the same value
timespec write_latency;
write_latency.tv_sec=0; write_latency.tv_nsec=50000000;
nanosleep(&write_latency,NULL);
write_latency.tv_nsec=100000000;
int i=100;
while(fabs(get_setpoint()-ct)>1e-4) {
if (i<0) throw Eurotherm2000Series_error("could not confirm setpoint value settings, timeout");
nanosleep(&write_latency,NULL);
--i;
}
return ct;
}
double Eurotherm2000Series::get_setpoint() const {
std::string answer;
read_value("SL",answer);
return strtod(answer.c_str(),NULL);
}
int Eurotherm2000Series::get_summary_status() const {
std::string answer;
read_value("SO",answer);
if (answer[0]!='>') throw Eurotherm2000Series_error("could not handle status data format \'"+answer+"\'");
answer.erase(0,1);
return strtol(answer.c_str(),NULL,16);
}
void Eurotherm2000Series::get_setpoint_limits(double& min, double& max) const {
std::string answer;
read_value("LS",answer);
min=strtod(answer.c_str(),NULL);
read_value("HS",answer);
max=strtod(answer.c_str(),NULL);
}

View File

@ -0,0 +1,161 @@
/* **************************************************************************
Author: Holger Stork, Achim Gaedke
Created: January 2005
****************************************************************************/
#ifndef EUROTHERM2000SERIES_H
#define EUROTHERM2000SERIES_H
#include <string>
#include <map>
#include "drivers/tempcont.h"
/**
\defgroup eurotherm2000series Eurotherm 2000 Series
\ingroup drivers
\brief remote control over serial interface
Wiring the serial interface is explained in Series 2000 Communications Handbook, Chapter 2.7.
This handbook is online available at http://www.eurotherm.com/comms/instcom.htm
The implemented EI-Bisync Protocol is described in caphter 4, the used parameter names are taken from Chapter 5
@{
*/
/**
special exception for Eurotherm 2000 drivers
*/
class Eurotherm2000Series_error: public tempcont_error
{
public:
explicit Eurotherm2000Series_error(const std::string& msg) throw (): tempcont_error(msg) {}
explicit Eurotherm2000Series_error(const char* msg) throw (): tempcont_error(msg) {}
virtual ~Eurotherm2000Series_error() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (Eurotherm2000Series_error): "; }
};
/**
this class communicates with Eurotherm 2200 Series temperature controlers
the contolers must be set to a known address, the serial line uses 19200baud, 7bit characters and even parity
there are various timeouts, that are hardcoded to detect transmition failures
*/
class Eurotherm2000Series: public tempcont
{
private:
/**
file descriptor for serial device with Eurotherm controler
*/
int serial_dev;
/**
device address, which must be set in Eurotherm device
*/
int address;
/**
serial device name
*/
std::string device_name;
/**
mask defining severe failures (abort of program could be triggered)
*/
int failure_mask;
/**
all these strange data formats of Bisynch: float values
*/
std::string fp_format;
/**
all these strange data formats of Bisynch: integer values with dot!
*/
std::string int_format;
/**
all these strange data formats of Bisynch: hexadecimal values
*/
std::string hex_format;
public:
/**
reads a value from Eurotherm
*/
void read_value(const std::string& param_name, std::string& return_value) const;
/**
sends a value to Eurotherm
*/
int set_value(const std::string& param_name, const std::string& value);
/**
send a list of configuration commands to eurotherm, make sure, that nobody uses eurotherm when calling this function
the configuration time is quite long (5 to 10 seconds) because of full device reset
*/
void configure(const std::map<std::string, std::string>& config);
void reset();
public:
/**
\brief initialise serial interface, test device communication, configure eurotherm
\param dev_name absolute path name of serial device used for communication
\param dev_address of Eurotherm device (to be set manualy in Eurotherm configuration)
\param failure_status_mask mask for status sumary to detect failures, see get_summary_status method
sets up the communication, configures Eurotherm2000 to <EFBFBD>K and nnn.n display format, tests the sensor and starts temperature history
*/
Eurotherm2000Series(const std::string& dev_name = std::string("/dev/ttyS0"), int dev_address = 1, int failure_status_mask = 32);
/**
*/
virtual ~Eurotherm2000Series();
/**
get the actual temperature
*/
virtual double get_temperature() const;
/**
set temperature setpoint value
\return temperature, that is set
*/
virtual double set_setpoint(double temperature);
/**
get temperature setpoint value
\return temperature, that is set
*/
virtual double get_setpoint() const;
/**
returns summary for eurotherm as hexadecimal value
the bits meanings are:
0: Alarm 1 State
1: Alarm 2 State
2: Alarm 3 State
3: Alarm 4 State
4: Manual Mode
5: Sensor Break
6: Loop Break
7: Heater Fail
8: Load Fail
9: Ramp/Program Complete
10: Process Variable out of Range
11: SSR Fail
12: New Alarm
13: Remote Input Sensor Break
*/
virtual int get_summary_status() const;
/**
get the low and high limits for setpoints
*/
virtual void get_setpoint_limits(double& min, double& max) const;
};
/**
@}
*/
#endif /* EUROTHERM2000SERIES_H */

View File

@ -0,0 +1,37 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: January 2005
#
#############################################################################
CXX=g++
CXXFLAGS=-O0 -g -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I. -I../..
LIBS=-lpthread -lxerces-c
.PHONY: all clean
all: Eurotherm-2000Series.o
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
../tempcont.o: ../tempcont.h ../tempcont.cpp
$(MAKE) -C .. tempcont.o
Eurotherm-2000Series.o: Eurotherm-2000Series.cpp Eurotherm-2000Series.h ../tempcont.h
$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) $< -o $@
test.exe: test.cpp Eurotherm-2000Series.o Eurotherm-2000Series.h ../tempcont.o
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o $(LIBS)
control: control.cpp Eurotherm-2000Series.o Eurotherm-2000Series.h ../tempcont.o ../../machines/hardware.o ../../core/core.a
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o ../../machines/hardware.o ../../core/core.a -lexpat $(LIBS)
gnuplot_output.exe: gnuplot_output.cpp Eurotherm-2000Series.o ../tempcont.o
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $< Eurotherm-2000Series.o ../tempcont.o $(LIBS)
clean: ../../tools/add_endline.exe
for f in Eurotherm-2000Series.cpp Eurotherm-2000Series.h test.cpp Makefile; do ../../tools/add_endline.exe $$f; done; \
rm -f *~ *.o *.exe *.stackdump core.{0,1,2,3,4,5,6,7,8,9}*;

View File

@ -0,0 +1,71 @@
#include "Eurotherm-2000Series.h"
#include <cstdlib>
#include <cstdio>
int main(int argc, char** argv) {
try {
Eurotherm2000Series e("/dev/ttyS0", 2);
double upper;
double lower;
e.get_setpoint_limits(lower,upper);
fprintf(stderr, "setpoint limits %f %f\n",lower,upper);
while (1) {
char buffer[100];
char* nextline=fgets(buffer,sizeof(buffer),stdin);
if (nextline==NULL) {
break;
}
if (nextline[0]==0) {
break;
}
else if (nextline[0]==10 || nextline[0]==12) {
// print the temperature and setpoint
double t=e.get_temperature();
double s=e.get_setpoint();
printf("%f %f\n", t,s);
}
else if (nextline[0]=='h') {
//print history
temp_history* h=e.get_history(0);
if (h==NULL) break;
for (temp_history::const_reverse_iterator i=h->rbegin();
i!=((temp_history::const_reverse_iterator)h->rend());
++i)
printf("%f\n",*i);
delete h;
printf("\n");
}
else if (nextline[0]=='a') {
// autotune
e.set_value("AT","0000.");
e.set_value("mA","0000.");
}
else if (nextline[0]=='r') {
// reset
e.reset();
}
else {
// get number and set setpoint
char* lineend=NULL;
double s=strtod(nextline,&lineend);
if (nextline!=lineend) {
fprintf (stderr,"new setpoint %f...",s);
fflush(stderr);
e.set_setpoint(s);
fprintf (stderr,"...done\n");
}
}
}
}
catch (Eurotherm2000Series_error e) {
fprintf(stderr,"%s\n",e.c_str());
return 1;
}
return 0;
}

View File

@ -0,0 +1,24 @@
#include "Eurotherm-2000Series.h"
#include <cstdio>
int main() {
try {
Eurotherm2000Series e("/dev/ttyS1",2);
while (1) {
if (e.device_failure()) return 1;
sleep(2);
temp_history* h=e.get_history(0);
printf("plot '-' w l\n");
for (temp_history::const_reverse_iterator i=h->rbegin(); i!=((temp_history::const_reverse_iterator)h->rend()); ++i)
printf("%f\n",*i);
delete h;
printf("e\n");
fflush(stdout);
}
}
catch (Eurotherm2000Series_error e) {
fprintf(stderr, "error: %s",e.c_str());
return 1;
}
return 0;
}

View File

@ -0,0 +1,18 @@
/* **************************************************************************
Author: Holger Stork, Achim Gaedke
Created: January 2005
****************************************************************************/
#include "Eurotherm-2000Series.h"
int main() {
try {
Eurotherm2000Series eurotherm("/dev/ttyS0",2);
}
catch (Eurotherm2000Series_error e) {
fprintf(stderr,"error: %s\n",e.c_str());
}
return 0;
}

47
drivers/Makefile Normal file
View File

@ -0,0 +1,47 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
CXX=g++
CXXFLAGS=-O0 -g -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I. -I..
#ToDo PBP-HS3-test.exe
SUBDIRS:=SpinCore-PulseBlaster SpinCore-PulseBlaster24Bit SpinCore-PulseBlasterDDSIII PTS-Synthesizer Eurotherm-2000Series dummy Spectrum-MI40xxSeries Spectrum-M2i40xxSeries Tecmag-DAC20
ifeq ($(shell /bin/uname -o),Cygwin)
SUBDIRS+=TiePie-HS3 Datel-PCI416
endif
all: tempcont.o
for i in $(SUBDIRS); do make -C $$i all; done
../tools/add_endline.exe: ../tools/add_endline.cpp
$(CXX) $< -o $@
.PHONY: SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.o TiePie-HS3/TiePie-HS3.a Eurotherm-2000Series/Eurotherm-2000Series.o clean install
tempcont.o: tempcont.cpp tempcont.h
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c $< -o $@
SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.o:
$(MAKE) -C SpinCore-PulseBlasterDDSIII SpinCore-PulseBlasterDDSIII.o
Eurotherm-2000Series/Eurotherm-2000Series.o:
$(MAKE) -C Eurotherm-2000Series Eurotherm-2000Series.o
TiePie-HS3/TiePie-HS3.a:
$(MAKE) -C TiePie-HS3 TiePie-HS3.a
Datel-PCI416/Datel-PCI416.o:
$(MAKE) -C Datel-PCI416 Datel-PCI416.o
clean: ../tools/add_endline.exe
for f in ADC.h frequgen.h pulsegen.h tempcont.h ; do ../tools/add_endline.exe $$f; done; \
rm -f *~ *.o *.exe *.stackdump core.{0,1,2,3,4,5,6,7,8,9}*; \
for i in $(SUBDIRS); do make -C $$i clean; done
install: all
for i in $(SUBDIRS); do make PREFIX=$(PREFIX) -C $$i install; done

View File

@ -0,0 +1,34 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: October 2004
#
#############################################################################
CXX=g++
CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I. -I../..
LDFLAGS=../../core/core.a -lexpat
.PHONY: all clean install
all: PTS.o PTS_test.exe
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
../../core/core.a:
$(MAKE) -C ../../core core.a
PTS.o: PTS.cpp ../frequgen.h
$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $<
PTS_test.o: PTS_test.cpp PTS.h
$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $<
PTS_test.exe: PTS_test.o PTS.o
$(CXX) -o $@ $^ $(LDFLAGS)
clean: ../../tools/add_endline.exe
for f in PTS.cpp PTS.h PTS_test.cpp; do $< $$f; done
rm -f *.exe *.o *~ core.*

View File

@ -0,0 +1,273 @@
/* ***************************************************************************
Author: Achim Gädke
Date: October 2004
**************************************************************************** */
#include "PTS.h"
#include <cstdio>
#include <cmath>
#ifdef __CYGWIN__
# define floorl floor
#endif
PTS::PTS(int myid): id(myid) {
frequency=0;
phase=0;
negative_logic=1;
phase_step=0.225;
}
PTS::~PTS() {
}
unsigned int PTS::phase_ttl_values(double p) const {
// break down to [0;360[
fprintf(stderr,"PHASE %g\n",phase_step);
p-=floor(p/360.0)*360.0;
unsigned int phasesteps=(unsigned int)fabs(round(p/phase_step));
int bcd_part=phasesteps/100;
unsigned int ttl_value=bcd_part<<8;
phasesteps-=bcd_part*100;
bcd_part=phasesteps/10;
ttl_value|=bcd_part<<4;
phasesteps-=bcd_part*10;
ttl_value|=phasesteps;
return ttl_value;
}
void PTS::phase_add_ttls(state& the_state, double p) const {
unsigned int binary_code=phase_ttl_values(p);
std::vector<ttlout>::const_iterator mask=ttl_masks.begin();
while (mask!=ttl_masks.end()) {
/* obeye negative logic */
if ((binary_code & 1<<11)==0 ^ negative_logic==0) the_state.push_back(mask->copy_new());
binary_code<<=1;
binary_code&=0xFFF;
++mask;
}
if (binary_code!=0) fprintf(stderr,"Warning! Insufficient phase precision for %f\n",p);
}
long unsigned int PTS::frequency_ttl_values(double f) const {
long unsigned int frequency_int=(long unsigned int)fabs(floor(f));
long unsigned int freq_bcd=0;
if (sizeof(freq_bcd)<8) {
fprintf(stderr, "warning! possible overflow (f=%f)\n", f);
}
for (long unsigned int divider=100000000; divider>0; divider/=10) {
int part_freq=frequency_int/divider;
freq_bcd<<=4;
freq_bcd|=part_freq;
frequency_int-=part_freq*divider;
}
return freq_bcd;
}
void PTS::set_frequency(double f) {
frequency=f;
/* todo switch PMD Module to frequency */
}
void PTS::set_frequency(state& experiment) {
// find the frequency informations...
// and exchange the phase informations
frequency=0;
phase=0;
state_sequent* exp_sequence=dynamic_cast<state_sequent*>(&experiment);
if (exp_sequence==NULL)
set_frequency_ttls(experiment);
else {
state_iterator si(*exp_sequence);
state* a_state=(state*)si.get_state();
while (NULL!=a_state) {
set_frequency_ttls(*a_state);
a_state=(state*)si.next_state();
} /* state loop */
}
}
void PTS::set_frequency_ttls(state& the_state) {
// find the frequency informations...
// and exchange the phase informations
analogout* pts_aout=NULL;
/* find a analogout section with suitable id */
state::iterator i=the_state.begin();
while(i!=the_state.end()) {
analogout* aout=dynamic_cast<analogout*>(*i);
if (aout!=NULL && aout->id==id) {
if (pts_aout==NULL) {
/* save the informations */
pts_aout=aout;
}
else {
fprintf(stderr, "found another pts decade section, ignoring\n");
delete aout;
}
/* remove the analog out section */
the_state.erase(i++);
}
else
++i;
} /* state members loop */
/* now, add the ttl information*/
if (pts_aout!=NULL) {
phase_add_ttls(the_state, pts_aout->phase);
phase=pts_aout->phase;
if (pts_aout->frequency!=0) {
if (frequency==0) {
set_frequency(pts_aout->frequency);
}
/* different frequencies are forbidden */
else if (frequency!=pts_aout->frequency) {
fprintf(stderr, "ignoring frequency %g at analogout %d\n",pts_aout->frequency,id);
}
}
delete pts_aout;
}
else {
/* because we use transparent mode, we have to set phase everywhere */
phase_add_ttls(the_state, phase);
}
}
/* **************************************************************************
PTS_latched
************************************************************************** */
void PTS_latched::set_frequency(state& experiment) {
// find the frequency informations...
// and exchange the phase informations
frequency=0;
phase=0;
state_sequent* exp_sequence=dynamic_cast<state_sequent*>(&experiment);
if (exp_sequence==NULL)
// is a very state on top level, todo: change interface
throw frequ_exception("cannot work on a single state, sorry (todo: change interface)");
else {
for(state_sequent::iterator child_state=exp_sequence->begin(); child_state!=exp_sequence->end(); ++child_state)
set_frequency_recursive(*exp_sequence, child_state);
}
}
void PTS_latched::set_frequency_recursive(state_sequent& the_sequence, state::iterator& the_state) {
state_sequent* a_sequence=dynamic_cast<state_sequent*>(*the_state);
if (a_sequence!=NULL) {
for(state_sequent::iterator child_state=a_sequence->begin(); child_state!=a_sequence->end(); ++child_state)
set_frequency_recursive(*a_sequence, child_state);
}
else {
// find the frequency informations...
state* this_state=dynamic_cast<state*>(*the_state);
if (this_state==NULL) throw frequ_exception("state_atom in state_sequent not expected");
// and exchange the phase informations
analogout* pts_aout=NULL;
/* find a analogout section with suitable id */
state::iterator i=this_state->begin();
while(i!=this_state->end()) {
analogout* aout=dynamic_cast<analogout*>(*i);
if (aout!=NULL && aout->id==id) {
if (pts_aout==NULL) {
/* save the informations */
pts_aout=aout;
}
else {
fprintf(stderr, "found another pts decade section, ignoring\n");
delete aout;
}
/* remove the analog out section */
this_state->erase(i++);
}
else
++i;
} /* state members loop */
if (pts_aout!=NULL) {
if (this_state->length<9e-8*4.0) throw frequ_exception("time is too short to save phase information");
/* now, insert the ttl information*/
/* set phase everytime */
pts_aout->phase-=floor(pts_aout->phase/360.0)*360.0;
int phase_int=(int)floor(pts_aout->phase/phase_step+0.5);
/* copy of original state */
state* register_state=new state(*this_state);
ttlout* register_ttls=new ttlout();
register_ttls->id=0;
register_state->length=9e-8;
register_state->push_back(register_ttls);
/* set frequency if necessary */
if (pts_aout->frequency!=0) {
if (this_state->length<9e-8*12.0) throw frequ_exception("time is too short to save frequency information");
// enable remote frequency control
register_ttls->ttls=0x8010;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
// in 0.1 Hz units !
unsigned long int frequency_int=(unsigned long int)floorl(fabs(pts_aout->frequency)*10.0+0.5);
if (sizeof(frequency_int)<8 && pts_aout->frequency>4e8) {
fprintf(stderr, "warning! possible overflow (f=%f)\n",pts_aout->frequency);
}
/* 100MHz and 10MHz */
register_ttls->ttls=(frequency_int/1000000000)%10<<8|((frequency_int/100000000)%10)<<4|15<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* 1MHz and 100kHz */
register_ttls->ttls=(frequency_int/10000000)%10<<8|((frequency_int/1000000)%10)<<4|14<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* 10kHz and 1kHz */
register_ttls->ttls=(frequency_int/100000)%10<<8|((frequency_int/10000)%10)<<4|13<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* 100Hz and 10Hz */
register_ttls->ttls=(frequency_int/1000)%10<<8|((frequency_int/100)%10)<<4|12<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* 1Hz and 0.1Hz (not used for all models) */
//register_state->length=0.5e-6;
register_ttls->ttls=(frequency_int/10)%10<<8|((frequency_int)%10)<<4|11<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
//register_state->length=9e-8;
/* and shorten the remaining state */
this_state->length-=9e-8*12;
//this_state->length-=9e-8*11+0.5e-6;
}
if (1) {
/* first entry for phase */
register_ttls->ttls=((phase_int/100)%16)<<4|9<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* second entry for phase */
register_ttls->ttls=(phase_int%10)<<8|((phase_int/10)%10)<<4|10<<12;
the_sequence.insert(the_state,register_state->copy_new());
register_ttls->ttls&=0x7fff;
the_sequence.insert(the_state,register_state->copy_new());
/* and shorten the remaining state */
this_state->length-=9e-8*4;
}
delete register_state;
delete pts_aout;
}
/* end of state modifications */
}
}

View File

@ -0,0 +1,127 @@
/* ***************************************************************************
Author: Achim Gaedke
Created: October 2004
**************************************************************************** */
#ifndef PTS_H
#define PTS_H
#include "drivers/frequgen.h"
#include <vector>
#include "core/states.h"
/**
\defgroup PTSSynthesizer PTS Frequency Synthesizer
\ingroup drivers
\brief driver for %PTS Frequency Synthesizer phase and frequency control
@{
*/
/**
\brief PTS Synthesizer driver for phase and preselected frequency
The driver can switch phase during experiment, the accuracy is determined by the number of connected phase selector lines.
Frequency support will be soon available, one frequency can be selected, which is configured before the experiment.
The Synthesizer is driven in transperent mode, i.e. no latch is used. Therefore phase information is added to all states.
If no phase is defined by an analogout instruction, the default phase is taken.
*/
class PTS: public frequgen {
public:
/**
\brief vector with the ttl output commands, that are added, to achieve the phase shift
Tuples of 4 masks are for 22.5, 2.25 and 0.225 degree increments, each representing weights of 8, 4, 2 and 1.
the most significant bit (180 degree) is the first member of the vector, significance is decreasing
*/
std::vector<ttlout> ttl_masks;
/**
\brief the assigned id of this analogout section
*/
int id;
/**
if negative logic should be used, this has to be set for noninverting cable drivers
*/
int negative_logic;
/**
define the phase stepping: PTS310 0.225 PTS500 0.36
*/
float phase_step;
/**
\brief default constructor
*/
PTS(int myid=0);
/**
\brief transform phase to ttl values, return them in unsigned int lower bits: 0 - 11
*/
unsigned int phase_ttl_values(double phase) const;
/**
\brief transform frequency to ttl values, return them in long unsigned int bits: 0 - 40
*/
long unsigned int frequency_ttl_values(double frequency) const;
/**
\brief add ttl instructions according to the phase
*/
void phase_add_ttls(state& the_state, double phase) const;
/**
\brief sets the reference frequency for the experiment time
*/
virtual void set_frequency(double f);
/**
\brief exchange analogout sections with phase TTLs and determines the experiments reference frequency
*/
virtual void set_frequency_ttls(state& experiment);
/**
\brief exchange analogout sections in all substates
*/
virtual void set_frequency(state& experiment);
/**
\brief destructor
nothing to do now... will change with frequency control
*/
virtual ~PTS();
};
/**
*/
class PTS_latched: public PTS {
public:
PTS_latched(int myid=0): PTS(myid) {}
/**
\brief exchange analogout sections with frequency and phase TTLs
*/
virtual void set_frequency_recursive(state_sequent& the_sequence, state_sequent::iterator& the_state);
/**
\brief exchange analogout sections in all substates
*/
virtual void set_frequency(state& experiment);
/**
\brief destructor
nothing to do now... will change with frequency control
*/
virtual ~PTS_latched(){}
};
/**
@}
*/
#endif

View File

@ -0,0 +1,122 @@
/* ***************************************************************************
Author: Achim Gädke
Created: October 2004
**************************************************************************** */
#include "PTS.h"
#include <cstdio>
#include "core/xml_states.h"
class PTS_test {
public:
PTS p;
PTS_test() {
p.id=0;
p.set_frequency(0);
ttlout t;
t.ttls=std::bitset<32>(1<<10);
p.ttl_masks.push_back(t);
t.ttls=std::bitset<32>(1<<9);
p.ttl_masks.push_back(t);
if (0) {
t.ttls=std::bitset<32>(1<<8);
p.ttl_masks.push_back(t);
t.ttls=std::bitset<32>(1<<7);
p.ttl_masks.push_back(t);
}
}
void phase_digit_translation_test() {
printf("100: %x\n",p.phase_ttl_values(100));
printf("-260: %x\n",p.phase_ttl_values(-360+100));
printf("0: %x\n",p.phase_ttl_values(0));
printf("90: %x\n",p.phase_ttl_values(90));
printf("180: %x\n",p.phase_ttl_values(180));
printf("270: %x\n",p.phase_ttl_values(270));
}
void phase_add_test() {
state s(1,NULL);
p.phase_add_ttls(s,270.5);
xml_state_writer().write_states(stdout,s);
}
void state_modify_test() {
state s(1);
analogout aout;
aout.phase=90;
aout.frequency=2e7;
s.push_back(aout.copy_new());
p.set_frequency(s);
xml_state_writer().write_states(stdout,s);
}
void sequent_iterate_test() {
state_sequent ss;
state s(1e-3,&ss);
ss.push_back(s.copy_new());
s.length=1;
analogout aout;
aout.phase=90;
aout.frequency=2e7;
s.push_back(aout.copy_new());
ss.push_back(s.copy_new());
delete s.front();
aout.frequency=2e6;
s.front()=aout.copy_new();
ss.push_back(s.copy_new());
p.set_frequency(ss);
xml_state_writer().write_states(stdout,ss);
}
void freq_digit_translation_test() {
printf("270e6: %lx\n",p.frequency_ttl_values(270e6));
}
};
class PTS_latched_test {
public:
PTS_latched p;
PTS_latched_test() {
p.id=0;
}
void test_simple() {
state_sequent ss;
state s(1e-3,&ss);
ss.push_back(s.copy_new());
s.length=2e-6;
analogout aout;
aout.phase=90.225;
aout.frequency=0;
s.push_back(aout.copy_new());
ss.push_back(s.copy_new());
delete s.front();
aout.phase=190;
aout.frequency=2e6;
s.front()=aout.copy_new();
ss.push_back(s.copy_new());
xml_state_writer().write_states(stdout,ss);
p.set_frequency(ss);
xml_state_writer().write_states(stdout,ss);
}
};
int main() {
PTS_latched_test().test_simple();
//PTS_test().freq_digit_translation_test();
return 0;
}

View File

@ -0,0 +1,117 @@
#include "GatedData.h"
#include <cstring>
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
/*
size of all childrens' data
*/
size_t DataManagementNode::size() const {
if (child==NULL)
return n;
size_t size_n=0;
for (const DataManagementNode* i=child; i!=NULL; i=i->next)
size_n+=i->size();
return size_n*n;
}
/*
reduce depth recursively
*/
DataManagementNode* DataManagementNode::reduce() {
// simple case: no children
if (child==NULL)
return this;
// evaluate all children's nodes and append remaining
DataManagementNode* i=child;
DataManagementNode* last_child=NULL;
while (i->next!=NULL) {
DataManagementNode* reduced_child=i->reduce();
if (reduced_child->n!=0) {
if (last_child==NULL)
child=reduced_child;
else
last_child->next=reduced_child;
last_child=reduced_child;
last_child->parent=this;
last_child->next=NULL;
}
DataManagementNode* next_child=i->next;
// throw away unused one
if (reduced_child->n==0 || i!=reduced_child) {
i->parent=NULL;
i->next=NULL;
delete i;
}
i=next_child;
}
// now look at the remaining list
if (child==NULL) {
// return empty node
n=0;
} else
// reduce depth by one
if (child->next==NULL || child->child!=NULL) {
n*=child->n;
DataManagementNode* superficial=child;
child=child->child;
superficial->parent=NULL;
delete superficial;
}
return this;
}
void DataManagementNode::print(FILE* f,size_t indent) {
char* indent_space=new char[indent+1];
memset(indent_space,' ',indent);
indent_space[indent]=0;
if (child==NULL)
fprintf(f,"%s%" SIZETPRINTFLETTER " samples\n",indent_space, n);
else {
fprintf(f,"%s%" SIZETPRINTFLETTER ":\n",indent_space, n);
child->print(f,indent+6);
}
delete indent_space;
if (next!=NULL)
next->print(f,indent);
}
DataManagementNode::DataManagementNode(const DataManagementNode& orig) {
parent=NULL;
if (orig.child!=NULL) {
child=new DataManagementNode(*orig.child);
for (DataManagementNode* i=child; i!=NULL; i=i->next) i->parent=this;
}
if (orig.next!=NULL) next=new DataManagementNode(*orig.next);
n=orig.n;
}
/**
recursive destruction
*/
DataManagementNode::~DataManagementNode() {
while (child!=NULL) {
DataManagementNode* this_one=child;
child=child->next;
this_one->parent=NULL;
this_one->next=NULL;
delete this_one;
}
}

View File

@ -0,0 +1,50 @@
#include <cstddef>
#include <cstdio>
class DataManagementNode;
/**
manages nested ADC data structure
*/
class DataManagementNode {
public:
/// parent node, top node has NULL
DataManagementNode* parent;
/// reference to nested loops
DataManagementNode* child;
/// next node in sequences
DataManagementNode* next;
/**
if the child is NULL, n is the number of real samples
otherwise repeatition number of substructure
*/
size_t n;
/**
default constructor of NOP node
*/
DataManagementNode(class DataManagementNode* par=NULL): parent(par), child(NULL), next(NULL), n(0) {}
/**
(deep) copy constructor
*/
DataManagementNode(const DataManagementNode& orig);
/**
size of all childrens' data
*/
size_t size() const;
/**
reduce depth recursively
*/
DataManagementNode* reduce();
void print(FILE* f,size_t indent=0);
/**
recursive destruction, call only for root
*/
~DataManagementNode();
};

View File

@ -0,0 +1,37 @@
CPPFLAGS=-Iinclude -I../..
CXXFLAGS=-Wshadow -Wall -O0 -g -DSPC_DEBUG=0
AR=ar
LIBS+= -lpthread -lm -lxerces-c -lexpat
SPC_HEADERS = include/spcerr.h include/regs.h include/dlltyp.h include/spcioctl.inc
SPC_ZIP = drv_spcm_linux_drv_v214b5633.zip
# SPC_ZIP = drv_new.zip
all: clean $(SPC_HEADERS) Spectrum-M2i40xxSeries.a
$(SPC_HEADERS): $(SPC_ZIP)
unzip -u $< "*.h" "*.txt" "*.inc" -d include/
Spectrum-M2i40xxSeries.a: Spectrum-M2i40xxSeries.o GatedData.o
$(AR) r $@ $^
Spectrum-M2i40xxSeries.o: Spectrum-M2i40xxSeries.cpp Spectrum-M2i40xxSeries.h GatedData.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
GatedData.o: GatedData.cpp GatedData.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
test.o: test.cpp Spectrum-M2i40xxSeries.h include/spcerr.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
test: test.o Spectrum-M2i40xxSeries.a ../../core/core.a
# $(CXX) -o $@ -I../.. ../ADC.h $^ $(LIBS)
$(CXX) -o $@ $(LIBS) -I../ADC.h $^
../../core/core.a:
$(MAKE) -C ../../core core.a
clean:
rm -f *.o *.a *~ test *.core
rm -rf include

View File

@ -0,0 +1,607 @@
/* Spectrum-M2i40xxSeries.cpp
Author: Stefan Reutter (2011)
*****************************************************************
Driver for the Spectrum M2i Series ADC cards. Based on the driver for the old MI40xx cards.
*/
#include <cmath>
#include <stack>
#include <cerrno>
#include <cstring>
#include "pthread.h"
#include "core/core.h"
#include "core/stopwatch.h"
#include "core/result.h"
#include "core/xml_states.h"
#include "Spectrum-M2i40xxSeries.h"
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
bool SpectrumM2i40xxSeries::Configuration::bOpenCard() {
// open ADC handle and read information to be displayed
char szDrvName[20];
sprintf (szDrvName, "/dev/spcm%d", lCardID);
hDrv = spcm_hOpen(szDrvName);
if (!hDrv) {
return false;
}
// read information to be displayed
spcm_dwGetParam_i32(hDrv, SPC_PCITYP, &lCardType);
spcm_dwGetParam_i32(hDrv, SPC_PCISERIALNO, &lSerialNumber);
spcm_dwGetParam_i32(hDrv, SPC_PCIFEATURES, &lFeatureMap);
spcm_dwGetParam_i64(hDrv, SPC_PCIMEMSIZE, &llInstMemBytes);
spcm_dwGetParam_i32(hDrv, SPC_MIINST_MINADCLOCK, &lMinSampleRate);
spcm_dwGetParam_i32(hDrv, SPC_MIINST_MAXADCLOCK, &lMaxSampleRate);
spcm_dwGetParam_i32(hDrv, SPC_MIINST_MODULES, &lModulesCount);
spcm_dwGetParam_i32(hDrv, SPC_MIINST_CHPERMODULE, &lMaxChannels);
spcm_dwGetParam_i32(hDrv, SPC_MIINST_BYTESPERSAMPLE, &lBytesPerSample);
spcm_dwGetParam_i32(hDrv, SPC_GETDRVVERSION, &lLibVersion);
spcm_dwGetParam_i32(hDrv, SPC_GETKERNELVERSION, &lKernelVersion);
int32 lTmp;
spcm_dwGetParam_i32 (hDrv, SPC_PCIVERSION, &lTmp);
lBaseHwVersion = (lTmp >> 16) & 0xffff;
lCtrlFwVersion = lTmp & 0xffff;
spcm_dwGetParam_i32 (hDrv, SPC_PCIMODULEVERSION, &lTmp);
lModHwVersion = (lTmp >> 16) & 0xffff;
lModFwVersion = lTmp & 0xffff;
// we need to recalculate the channels value as the driver returns channels per module
lMaxChannels *= lModulesCount;
int32 lAIFeatures;
spcm_dwGetParam_i32(hDrv, SPC_MIINST_BITSPERSAMPLE, &lResolution);
spcm_dwGetParam_i32(hDrv, SPC_READAIPATHCOUNT, &lPathCount);
spcm_dwGetParam_i32(hDrv, SPC_READIRCOUNT, &lRangeCount);
for (int i=0; (i<lRangeCount) && (i<SPCM_MAX_AIRANGE); i++) {
spcm_dwGetParam_i32(hDrv, SPC_READRANGEMIN0 + i, &lRangeMin[i]);
spcm_dwGetParam_i32(hDrv, SPC_READRANGEMAX0 + i, &lRangeMax[i]);
}
spcm_dwGetParam_i32(hDrv, SPC_READAIFEATURES, &lAIFeatures);
bInputTermAvailable = (lAIFeatures & SPCM_AI_TERM) != 0;
bDiffModeAvailable = (lAIFeatures & SPCM_AI_DIFF) != 0;
bACCouplingAvailable =(lAIFeatures & SPCM_AI_ACCOUPLING) != 0;
bBWLimitAvailable = (lAIFeatures & SPCM_AI_LOWPASS) != 0;
bOffsPercentMode = (lAIFeatures & SPCM_AI_OFFSPERCENT) != 0;
spcm_dwGetParam_i32(hDrv, SPC_MIINST_MAXADCVALUE, &lMaxADCValue);
// set up gated sampling mode
spcm_dwSetParam_i32(hDrv, SPC_CARDMODE, SPC_REC_STD_GATE);
//spcm_dwSetParam_i32(hDrv, SPC_CARDMODE, SPC_REC_STD_SINGLE);
// set up external TTL trigger
spcm_dwSetParam_i32(hDrv, SPC_TRIG_ORMASK, SPC_TMASK_EXT0);
spcm_dwSetParam_i32(hDrv, SPC_TRIG_EXT0_MODE, SPC_TM_POS);
//spcm_dwSetParam_i32(hDrv, SPC_PRETRIGGER, 16384);
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_CH_ORMASK0, SPC_TMASK0_CH0);
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_CH0_MODE, SPC_TM_POS);
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_TERM, 1);
//spcm_dwSetParam_i32(hDrv, SPC_TRIG_DELAY, 2000);
//spcm_dwSetParam_i32(hDrv, SPC_TIMEOUT, 5000);
return true;
}
char* SpectrumM2i40xxSeries::Configuration::PrintInfo() {
char *sInfo = new char[10000];
char *sTmp = new char[100];
// the card type + serial number
switch (lCardType & TYP_SERIESMASK){
case TYP_M2ISERIES: sprintf(sInfo, "M2i.%04x sn %05d\n",(unsigned int) (lCardType & TYP_VERSIONMASK), lSerialNumber); break;
case TYP_M2IEXPSERIES: sprintf(sInfo, "M2i.%04x-Exp sn %05d\n", (unsigned int)( lCardType & TYP_VERSIONMASK), lSerialNumber); break;
default: sprintf(sInfo, "Type: %x not supported so far\n", lCardType); break;
}
sprintf(sTmp, " Installed memory: %lld MByte\n", llInstMemBytes / 1024 / 1024);
strcat(sInfo, sTmp);
sprintf(sTmp, " Max sampling rate: %.1f MS/s\n", (double) lMaxSampleRate / 1000000);
strcat(sInfo, sTmp);
sprintf(sTmp, " Channels: %d\n", lMaxChannels);
strcat(sInfo, sTmp);
sprintf(sTmp, " Kernel Version: %d.%02d build %d\n", lKernelVersion >> 24, (lKernelVersion >> 16) & 0xff, lKernelVersion & 0xffff);
strcat(sInfo, sTmp);
sprintf(sTmp, " Library Version %d.%02d build %d\n", lLibVersion >> 24, (lLibVersion >> 16) & 0xff, lLibVersion & 0xffff);
strcat(sInfo, sTmp);
#if SPC_DEBUG
sprintf(sTmp, "Debug Information:\n");
strcat(sInfo, sTmp);
sprintf(sTmp, " Bytes per sample: %i\n", lBytesPerSample);
strcat(sInfo, sTmp);
#endif
return sInfo;
}
/*
Initialize the card
*/
SpectrumM2i40xxSeries::SpectrumM2i40xxSeries(const ttlout& t_line, int ext_reference_clock) {
// print neat string to inform the user
fprintf(stderr, "Initializing ADC card\n");
// check parameters
trigger_line=t_line;
default_settings.qwSetChEnableMap = channel_array(ADC_M2I_DEFAULT_CHANNELS);
default_settings.lSetChannels = default_settings.qwSetChEnableMap.count();
default_settings.impedance = new double[default_settings.lSetChannels];
default_settings.sensitivity = new double[default_settings.lSetChannels];
default_settings.offset = new int[default_settings.lSetChannels];
for(int i = 0; i < default_settings.lSetChannels; i++) {
default_settings.impedance[i] = ADC_M2I_DEFAULT_IMPEDANCE;
default_settings.sensitivity[i] = ADC_M2I_DEFAULT_SENSITIVITY; // Set sensitivity in Volts to the default (maximum) value
default_settings.offset[i] = ADC_M2I_DEFAULT_OFFSET; // Set offsets in % of sensitivity to default (0)
}
default_settings.ext_reference_clock = ext_reference_clock; // Hz
effective_settings=NULL;
fprintf(stderr, "clock %i\n", ext_reference_clock);
// initializing adc info class with card ID at 0
default_settings.lCardID = 0;
if(default_settings.bOpenCard())
fprintf(stderr, "%s", default_settings.PrintInfo());
else
throw SpectrumM2i40xxSeries_error("Could not open card");
spcm_dwSetParam_i32(default_settings.hDrv, SPC_CLOCKMODE, SPC_CM_EXTREFCLOCK);
spcm_dwSetParam_i32(default_settings.hDrv, SPC_REFERENCECLOCK, default_settings.ext_reference_clock);
spcm_dwSetParam_i32(default_settings.hDrv, SPC_CLOCK50OHM, 1); // ToDo: test this
fprintf(stderr, "\nADC card initialized\n");
}
void SpectrumM2i40xxSeries::collect_config_recursive(state_sequent& exp, SpectrumM2i40xxSeries::Configuration& settings) {
/* start with dummy node */
DataManagementNode* new_branch = new DataManagementNode(NULL);
DataManagementNode* where_to_append = new_branch;
double parent_timeout=settings.timeout;
settings.timeout=0.0;
/* loop over all states and sequences within the current sequence */
for (state_sequent::iterator i = exp.begin(); i != exp.end(); ++i) {
state* a_state = dynamic_cast<state*>(*i); // cast into a state and check for illegal types
if (a_state == NULL)
throw SpectrumM2i40xxSeries_error("Expecting state or state_sequent object");
if (dynamic_cast<state_parallel*>(*i)!=NULL)
throw SpectrumM2i40xxSeries_error("State parallel is not implemented");
state_sequent* a_sequence=dynamic_cast<state_sequent*>(a_state); // cast into a sequence of states
if (a_sequence != NULL) { // if a sequence is found, recurse
DataManagementNode* tmp_structure = settings.data_structure;
settings.data_structure = where_to_append;
collect_config_recursive(*a_sequence, settings);
settings.data_structure = tmp_structure;
} else { // otherwise, we are supposed to have a single analogin state
settings.timeout += a_state->length;
// collect analogin sections in state
std::list<analogin*> inputs;
// loop over analogin states for this device
state::iterator k = a_state->begin();
while (k != a_state->end()) {
analogin* input = dynamic_cast<analogin*>(*k);
if (input != NULL && input->id == device_id) { // check for validity
if (input->samples<=0 || input->sample_frequency<=0) { // don't use an input if it doesn't make sense
delete input;
} else {
inputs.push_back(input);
}
k=a_state->erase(k);
} else {
++k;
}
}
if (!inputs.empty()) {
/* evaluate the found analogin definitions */
if (inputs.size() > 1) { // only one analogin allowed for each state
while (!inputs.empty()) { delete inputs.front(); inputs.pop_front();} // free list
throw ADC_exception("can not handle more than one analogin section per state");
}
/* save sampling frequency */
if (settings.samplefreq <= 0) {
settings.samplefreq = inputs.front()->sample_frequency;
} else if (settings.samplefreq != inputs.front()->sample_frequency) {
while (!inputs.empty()) { delete inputs.front(); inputs.pop_front();} // free list
throw ADC_exception("Sorry, but gated sampling requires same sampling frequency in all analogin sections");
}
/* save sensitvity */
if (settings.sensitivity != NULL) { // if sensitivity is set, make sure it's valid (i.e. the same for all inputs)
for (int j = 0; j < inputs.front()->nchannels; j++) {
if (settings.sensitivity[j] != inputs.front()->sensitivity[j]) {
fprintf(stderr, "Warning! different sensitivity specified (here %f, elsewhere %f), choosing higher voltage\n",
settings.sensitivity[j],
inputs.front()->sensitivity[j]);
if (settings.sensitivity[j] < inputs.front()->sensitivity[j]) {
settings.sensitivity[j] = inputs.front()->sensitivity[j];
}
}
}
} else {
settings.sensitivity = inputs.front()->sensitivity;
}
// check if sensitivity is valid
for (int j = 0; j < inputs.front()->nchannels; j++) {
bool sensAllowed = false;
for (int l = 0; l < ADC_M2I_ALLOWED_SENSITIVITY_LENGTH; l++) {
if (settings.sensitivity[j] == ADC_M2I_ALLOWED_SENSITIVITY[l] ) {
sensAllowed = true;
break;
}
}
if (!sensAllowed) {
fprintf(stderr, "Warning! Illegal sensitivity specified for channel %i: %f", j, inputs.front()->sensitivity[j]);
settings.sensitivity[j] = ADC_M2I_DEFAULT_SENSITIVITY;
}
}
/* save impedance */
if (settings.impedance != NULL) {
for (int j = 0; j < inputs.front()->nchannels; j++) {
if (settings.impedance[j] != inputs.front()->impedance[j]) {
fprintf(stderr, "Warning! different impedance specified (here %f, elsewhere %f), setting to default\n",
settings.impedance[j],
inputs.front()->impedance[j]);
settings.impedance[j] = ADC_M2I_DEFAULT_IMPEDANCE;
}
if (settings.impedance[j] != ADC_M2I_DEFAULT_IMPEDANCE && settings.impedance[j] != ADC_M2I_ALLOWED_IMPEDANCE) {
fprintf(stderr, "Warning! Illegal impedance specified for channel %i: %f", j, inputs.front()->impedance[j]);
settings.offset[j] = 0;
}
}
} else {
settings.impedance = inputs.front()->impedance;
}
/* save offsets */
if (settings.offset != NULL) {
for (int j = 0; j < inputs.front()->nchannels; j++) {
if (settings.offset[j] != inputs.front()->offset[j]) {
fprintf(stderr, "Warning! different impedance specified (here %i, elsewhere %i), setting to default\n",
settings.offset[j],
inputs.front()->offset[j]);
settings.offset[j] = ADC_M2I_DEFAULT_OFFSET;
}
if (inputs.front()->offset[j] > 100 || inputs.front()->offset[j] < -100) {
fprintf(stderr, "Warning! Illegal offset specified for channel %i: %i", j, inputs.front()->offset[j]);
settings.offset[j] = 0;
}
}
} else {
settings.offset = inputs.front()->offset;
}
if (inputs.front()->samples%4 != 0) {
throw ADC_exception("Number of samples must be a multiple of four");
}
/* save channel mask and number of channels */
if (settings.lSetChannels > 0) {
if (settings.qwSetChEnableMap.to_ulong() > 0) {
if (settings.qwSetChEnableMap != inputs.front()->channels) {
fprintf(stderr, "Warning! different channels enabled in input %lu and in config %lu, setting to default \n",
settings.qwSetChEnableMap.to_ulong(),
inputs.front()->channels.to_ulong());
settings.qwSetChEnableMap = channel_array(ADC_M2I_DEFAULT_CHANNELS);
settings.lSetChannels = settings.qwSetChEnableMap.count();
}
} else {
settings.qwSetChEnableMap = inputs.front()->channels;
settings.lSetChannels = inputs.front()->nchannels;
}
} else {
settings.qwSetChEnableMap = inputs.front()->channels;
settings.lSetChannels = inputs.front()->nchannels;
}
// gating time offsets apparently were fixed in the M2i cards. todo: check
// calculate the time required
double time_required;
time_required = (inputs.front()->samples)/settings.samplefreq;
time_required = ceil(1e8*time_required)/1e8;
// check time requirements
if (a_state->length < time_required) {
char parameter_info[512];
snprintf(parameter_info,sizeof(parameter_info),
"(%" SIZETPRINTFLETTER " samples, %g samplerate, %e time required, %e state time)",
inputs.front()->samples,
settings.samplefreq,
time_required,
a_state->length);
// update the state length if it's shorter than the gate. this is usually due to rounding to 10 ns for the pulseblaster
if (ceil(1e8*a_state->length)/1e8 < time_required) {
throw ADC_exception(std::string("state is shorter than acquisition time")+parameter_info);
} else {
a_state->length = time_required;
}
}
// adapt the pulse program for gated sampling
if (a_state->length == time_required) { // state has proper length
a_state->push_back(trigger_line.copy_new());
} else { // state is too long...
// create new one with proper time and gated sampling pulse
state* gated_sampling_pulse = new state(*a_state);
gated_sampling_pulse->length = time_required;
gated_sampling_pulse->push_back(trigger_line.copy_new());
// insert gate pulse state before remaining (original) state
exp.insert(i,(state_atom*)gated_sampling_pulse);
// shorten this state
a_state->length -= time_required;
}
# if SPC_DEBUG
fprintf(stderr, "state sequence:\n");
xml_state_writer().write_states(stderr, exp);
# endif
/* insert a new state */
DataManagementNode* new_one = new DataManagementNode(new_branch);
new_one->n = inputs.front()->samples;
new_one->child = NULL;
new_one->next = where_to_append->next;
where_to_append->next = new_one;
where_to_append = new_one;
while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();} // free inputs
} /* !inputs.empty() */
} // end state
} // i
/* something happened? */
if (new_branch->next != NULL) {
/* make dummy node to a loop */
new_branch->n=exp.repeat;
new_branch->child=new_branch->next;
/* if possible, append it */
if (settings.data_structure!=NULL) {
new_branch->parent=settings.data_structure->parent;
new_branch->next=settings.data_structure->next;
settings.data_structure->next=new_branch;
} else {
new_branch->parent=NULL;
new_branch->next=NULL;
settings.data_structure=new_branch;
}
}
else
delete new_branch;
settings.timeout *= exp.repeat;
settings.timeout += parent_timeout;
fprintf(stderr,"setting.timeout %g\n",settings.timeout);
return;
}
void SpectrumM2i40xxSeries::set_daq(state & exp) {
fprintf(stderr, "Setting up data acquisition\n");
// check whether the experiment state is legal
state_sequent* exp_sequence=dynamic_cast<state_sequent*>(&exp);
if (exp_sequence==NULL)
throw SpectrumM2i40xxSeries_error("Spectrum-M2i40xxSeries::set_daq only working on sequences");
/* find out what to do */
Configuration* conf=new Configuration();
collect_config_recursive(*exp_sequence, *conf);
if (conf->samplefreq <= 0) conf->samplefreq = default_settings.samplefreq;
if (conf->impedance == NULL) conf->impedance = default_settings.impedance;
if (conf->sensitivity == NULL) conf->sensitivity = default_settings.sensitivity;
if (conf->offset == NULL) conf->offset = default_settings.offset;
if (conf->hDrv == NULL) conf->hDrv = default_settings.hDrv;
if (conf->lBytesPerSample == 0) conf->lBytesPerSample = default_settings.lBytesPerSample;
if (conf->lSetChannels == 0) { conf->lSetChannels = default_settings.lSetChannels; conf->qwSetChEnableMap = default_settings.qwSetChEnableMap; }
size_t sampleno = (conf->data_structure == NULL) ? 0 : conf->data_structure->size();
/* nothing to do! */
if (sampleno == 0) {
delete conf;
effective_settings=NULL;
fprintf(stderr, "Warning: Nothing to do.\n");
return;
}
if (sampleno < 16 || sampleno%16 != 0) {
delete conf;
throw SpectrumM2i40xxSeries_error("total number of samples must be multiple of 16 and at least 16");
}
effective_settings=conf;
// make sure the board is ready
int actual_status = 0;
spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &actual_status);
if ((actual_status & M2STAT_CARD_READY) == 0) {
fprintf(stderr, "Warning: Spectrum board was/is running before starting data aquisition. Status: %i\n", actual_status);
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_STOP); // stop the board
}
// set sensitivity, channels, etc.
for (int j = 0; j < effective_settings->lSetChannels; j++) {
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_AMP0 + 100*j, (int)floor(effective_settings->sensitivity[j]*1000)); // +/- 10V input range
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_50OHM0 + 100*j, ((effective_settings->impedance[j] == 50.0) ? 1 : 0)); // 1 = 50 Ohm input impedance, 0 = 1MOhm input impedance
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_OFFS0 + 100*j, effective_settings->offset[j]); // set offset in % of sensitivity
#if SPC_DEBUG
fprintf(stderr, "Input impedance for channel %i is %f\n", j, effective_settings->impedance[j]);
#endif
}
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_CHENABLE, effective_settings->qwSetChEnableMap.to_ulong()); // set channels
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_SAMPLERATE, (int)floor(effective_settings->samplefreq)); // set sample rate
// check if frequency was set correctly
int setSamplingRate = 0;
spcm_dwGetParam_i32(effective_settings->hDrv, SPC_SAMPLERATE, &setSamplingRate);
if (setSamplingRate != (int)floor(effective_settings->samplefreq)) {
char parameter_info[16];
snprintf(parameter_info,sizeof(parameter_info), "%d", setSamplingRate);
throw ADC_exception(std::string("DAC sampling rate not available. Try setting to: ")+parameter_info);
}
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_MEMSIZE, sampleno + ADC_M2I_PRETRIGGER + ADC_M2I_POSTTRIGGER); // Memory size * effective_settings->lSetChannels * effective_settings->lBytesPerSample
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_PRETRIGGER, ADC_M2I_PRETRIGGER);
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_POSTTRIGGER, ADC_M2I_POSTTRIGGER);
// ----- start the board -----
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_START | M2CMD_CARD_ENABLETRIGGER); // start the board
// check for error messages
char szErrorText[ERRORTEXTLEN];
if (spcm_dwGetErrorInfo_i32 (effective_settings->hDrv, NULL, NULL, szErrorText) != ERR_OK) {
if (effective_settings!=NULL) delete effective_settings;
effective_settings=NULL;
fprintf(stderr, "%s", szErrorText);
throw SpectrumM2i40xxSeries_error(szErrorText);
}
fprintf(stderr, "Data acquisition setup successful\n");
}
result* SpectrumM2i40xxSeries::get_samples(double _timeout) {
if (core::term_signal != 0) return NULL;
size_t sampleno = (effective_settings == NULL || effective_settings->data_structure == NULL) ? 0 : effective_settings->data_structure->size();
if (sampleno == 0) return new adc_result(1,0,NULL);
#if SPC_DEBUG
fprintf(stderr, "samples: %lu\tchannels: %i\tbytes/sample: %i\n", sampleno, effective_settings->lSetChannels, effective_settings->lBytesPerSample);
#endif
int memSize = (sampleno + ADC_M2I_PRETRIGGER + ADC_M2I_POSTTRIGGER) * effective_settings->lSetChannels * effective_settings->lBytesPerSample;
#if SPC_DEBUG
fprintf(stderr, "memory size: %i\n", memSize);
#endif
short int* adc_data=(short int*)malloc(memSize);
stopwatch adc_timer;
adc_timer.start();
int adc_status;
spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status);
fprintf(stderr, "card status: %x; waiting for trigger\n", adc_status);
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_TIMEOUT, 1000);
if (spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_WAITTRIGGER) == ERR_TIMEOUT) {
fprintf(stderr, "no trigger detected, timing out and forcing one now\n");
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_FORCETRIGGER);
}
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_TIMEOUT, 0);
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_WAITREADY);
/*while (core::term_signal == 0 && (adc_status & M2STAT_CARD_READY) == 0 && adc_timer.elapsed() <= effective_settings->timeout) {
timespec sleeptime;
sleeptime.tv_nsec = 10*1000*1000; // 10 ms
sleeptime.tv_sec = 0;
nanosleep(&sleeptime, NULL);
spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status);
fprintf(stderr, "card status: %x\n", adc_status);
}*/
fprintf(stderr, "ADC finished. Stopping\n");
spcm_dwSetParam_i32(effective_settings->hDrv, SPC_M2CMD, M2CMD_CARD_STOP);
if (core::term_signal != 0) {
free(adc_data);
return NULL;
}
spcm_dwGetParam_i32(effective_settings->hDrv, SPC_M2STATUS, &adc_status);
if ((adc_status & M2STAT_CARD_READY) == 0) {
free(adc_data);
fprintf(stderr, "adc_status not ready: %i \n", adc_status);
throw SpectrumM2i40xxSeries_error("timeout occured while collecting data");
}
fprintf(stderr, "Starting data transfer.\n");
spcm_dwDefTransfer_i64 (effective_settings->hDrv, SPCM_BUF_DATA, SPCM_DIR_CARDTOPC, 0, adc_data, 0, memSize);
spcm_dwSetParam_i32 (effective_settings->hDrv, SPC_M2CMD, M2CMD_DATA_STARTDMA | M2CMD_DATA_WAITDMA);
char szErrorText[ERRORTEXTLEN];
if (spcm_dwGetErrorInfo_i32 (effective_settings->hDrv, NULL, NULL, szErrorText)){
delete adc_data;
throw SpectrumM2i40xxSeries_error(szErrorText);
}
short int* data_position=adc_data + ADC_M2I_PRETRIGGER*effective_settings->lSetChannels; // drop first points due to pre trigger
// produced results
adc_results* the_results = new adc_results(0);
data_position = split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results);
if (data_position==0 || (size_t)(data_position - adc_data - ADC_M2I_PRETRIGGER * effective_settings->lSetChannels) != (sampleno * effective_settings->lSetChannels)) {
fprintf(stderr,"something went wrong while splitting data\n");
}
free(adc_data);
delete effective_settings;
effective_settings=NULL;
fprintf(stderr, "Finished data transfer.\n");
return the_results;
}
short int* SpectrumM2i40xxSeries::split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted) {
if (structure.child==NULL) {
// simple case: do real work
// todo: Channel selection
short int* datachunk = (short int*) malloc(effective_settings->lBytesPerSample*structure.n*effective_settings->lSetChannels);
if (datachunk==NULL) {
throw SpectrumM2i40xxSeries_error("not enough memory to create results");
}
// todo: error checking
memcpy(datachunk, data, effective_settings->lBytesPerSample*structure.n*effective_settings->lSetChannels);
data += structure.n*effective_settings->lSetChannels;
adc_result* the_result = new adc_result(0, structure.n, datachunk, effective_settings->samplefreq, effective_settings->lSetChannels);
result_splitted.push_back(the_result);
if (structure.next != NULL)
data = split_adcdata_recursive(data, *(structure.next), result_splitted);
} else {
for (size_t i=0; i<structure.n; ++i) {
data = split_adcdata_recursive(data, *(structure.child), result_splitted);
}
}
return data;
}
void SpectrumM2i40xxSeries::sample_after_external_trigger(double rate, size_t samples, double sensitivity, size_t resolution) {
throw SpectrumM2i40xxSeries_error("SpectrumM2i40xxSeries::sample_after_external_trigger is not implemented");
}
SpectrumM2i40xxSeries::~SpectrumM2i40xxSeries() {
spcm_vClose(default_settings.hDrv);
}

View File

@ -0,0 +1,277 @@
// todo: Multi-Platform support is not implemented.
// todo: fifo if needed
#ifndef SPECTRUM_M2i40XXSERIES
#define SPECTRUM_M2i40XXSERIES
#define SPCM_MAX_AIRANGE 8
#define SPCM_MAX_AICHANNEL 16
#include <string>
#include <vector>
#include <math.h>
#include "core/states.h"
#include "core/constants.h"
#include "drivers/ADC.h"
#include "GatedData.h"
#if defined __linux__
// ----- linux includes -----
# include <cstdio>
# include <fcntl.h>
# include <sys/ioctl.h>
# include <unistd.h>
# include "include/dlltyp.h"
# include "include/regs.h"
# include "include/spcerr.h"
# include "include/spcm_drv.h"
#endif
#if !(defined __linux__)
# error "sorry, expecting linux"
#endif
#ifndef SPC_DEBUG
# define SPC_DEBUG 0
#endif
/**
\defgroup spectrum2i40xxseries Spectrum M2i40xx ADC
\ingroup drivers
\brief The driver for the Spectrum M2i.40xx series analog to digital converter.
The M2i.4021/4022 we use supports sampling with up to 20 MS/s on 2/4 channels. This driver requires the GatedSampling option to be installed.
@{
*/
class SpectrumM2i40xxSeries_error: public ADC_exception
{
public:
explicit SpectrumM2i40xxSeries_error(const std::string& msg) throw (): ADC_exception(msg) {}
explicit SpectrumM2i40xxSeries_error(const char* msg) throw (): ADC_exception(msg) {}
virtual ~SpectrumM2i40xxSeries_error() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (SpectrumM2i40xxSeries_error): "; }
};
/**
\brief The driver class
*/
class SpectrumM2i40xxSeries: public ADC {
/**
ttlout triggerline device id
*/
int device_id;
/**
ttlout triggerline ttlmask
*/
ttlout trigger_line;
/** stuff concerning fifo acquisition */
size_t fifobufferno;
/** stuff concerning fifo acquisition */
size_t fifobufferlen;
/** stuff concerning fifo acquisition */
size_t fifo_minimal_size;
/** stuff concerning fifo acquisition */
std::vector<short int*> fifobuffers;
/** the extra data thread should store things to that buffer */
short int* fifo_adc_data;
pthread_attr_t timeout_pt_attrs;
pthread_t timeout_pt;
# if defined __linux__
// ----- include the easy ioctl commands from the driver -----
# include "include/spcioctl.inc"
# endif
/**
\brief Stores card settings and information
*/
class Configuration {
public:
/** sampling frequency in Hz */
double samplefreq;
/** Data returned by the ADC during the different gating periods is stored in a nested list */
DataManagementNode* data_structure;
/** a timeout for acquiring data in s*/
double timeout;
/** configured input impedance for each channel in Ohm*/
double* impedance;
/** configured input sensitivity for each channel in V*/
double* sensitivity;
/** coupling 0=DC else AC */
int coupling;
/** external reference clock **/
int ext_reference_clock;
/** offsets for each channel in % of sensitivity **/
int* offset;
// Information about the card copied from the Spectrum example program
/** \brief The card's driver handle as returned by the Spectrum driver open function */
drv_handle hDrv;
/** \brief The card's ID
* Typically 0 if no other card is installed. The device is created as /dev/spcm0
*/
int32 lCardID;
/** \brief The card's type as listed in the manual */
int32 lCardType;
/** \brief Serial Number */
int32 lSerialNumber;
/** \brief Installed on-board memory in bytes */
int64 llInstMemBytes;
/** \brief Bitmap with installed card features */
int32 lFeatureMap;
/** \brief Number of channels (analog or digital) */
int32 lMaxChannels;
/** \brief Number of bytes for each sample (analog data) */
int32 lBytesPerSample;
/** \brief Minimum sampling rate */
int32 lMinSampleRate;
/** \brief Maximum sampling rate */
int32 lMaxSampleRate;
/** \brief Number of installed modules for data sorting algorithm */
int32 lModulesCount;
/** \brief Library version */
int32 lLibVersion;
/** \brief Kernel version */
int32 lKernelVersion;
/** \brief Main control firmware version */
int32 lCtrlFwVersion;
/** \brief Base hardware version */
int32 lBaseHwVersion;
/** \brief Module hardware version */
int32 lModHwVersion;
/** \brief Module firmware version */
int32 lModFwVersion;
// current settings
/** \brief Bitmap for currently enabled channels */
channel_array qwSetChEnableMap;
/** \brief Programmed memory size */
int llSetMemsize;
/** \brief Number of used channels for this run */
int lSetChannels;
/** \brief Currently active oversampling factor */
int32 lOversampling;
// Analog Input settings
/** \brief Resolution of analog input channel */
int32 lResolution;
int32 lMaxADCValue; // maximum range, normally 2^(Resolution-1) but can be limited
int32 lPathCount; // number of input paths
int32 lRangeCount; // number of analog input ranges
int32 lRangeMin[SPCM_MAX_AIRANGE]; // analog input ranges
int32 lRangeMax[SPCM_MAX_AIRANGE]; // ...
bool bInputTermAvailable; // input termination available
bool bDiffModeAvailable; // differential mode available
bool bACCouplingAvailable; // AC/DC coupling softwar selectable
bool bBWLimitAvailable; // bandwidth limit available
bool bOffsPercentMode; // offset programmed in percent of range
/** print data for debug purpose */
void print(FILE* f);
bool bOpenCard();
char* PrintInfo();
Configuration() {
samplefreq=0;
timeout=0;
impedance = NULL;
sensitivity = NULL;
offset = NULL;
coupling=0;
ext_reference_clock=0;
data_structure=NULL;
hDrv = NULL;
lCardID = 0;
lSetChannels = 0;
lBytesPerSample = 0;
}
Configuration(const Configuration& orig) {
samplefreq=orig.samplefreq;
timeout=orig.timeout;
impedance=orig.impedance;
sensitivity=orig.sensitivity;
coupling=orig.coupling;
ext_reference_clock=orig.ext_reference_clock;
qwSetChEnableMap = orig.qwSetChEnableMap;
lSetChannels = orig.lSetChannels;
if (orig.data_structure==NULL)
data_structure=NULL;
else
data_structure=new DataManagementNode(*orig.data_structure);
}
~Configuration() {
if (data_structure!=NULL)
delete data_structure;
}
};
/**
\brief Default settings for the ADC card.
These settings are copied and modified at need to derive effective settings
*/
Configuration default_settings;
/**
\brief The settings actually used for a measurement.
Once a measurement is started, user settings may override default settings
*/
Configuration* effective_settings;
short int* split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted);
/**
\brief Collects the settings required for the current job.
*/
void collect_config_recursive(state_sequent& exp, SpectrumM2i40xxSeries::Configuration& settings);
public:
SpectrumM2i40xxSeries(const ttlout& t_line, int ext_reference_clock=(int)100e6);
virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity=5.0, size_t resolution=14);
virtual void set_daq(state & exp);
/**
for syncronization purposes with sample clock
*/
//double get_sample_clock_frequency() const;
/**
get results from transient recorder
timeout: 0.0 return immediately
*/
virtual result* get_samples(double timeout=0.0);
/**
timeout issues during fifo acquisition
*/
//int TimeoutThread();
virtual ~SpectrumM2i40xxSeries();
};
/** @{
*/
#endif

View File

@ -0,0 +1,67 @@
#include "Spectrum-M2i40xxSeries.h"
#include "../../machines/hardware.h"
#include "include/regs.h"
#include "core/states.h"
#include <typeinfo>
int main() {
int i = 3;
fprintf(stderr, "a"+i);
try {
SpectrumM2i40xxSeries* my_adc;
ttlout trigger;
trigger.id=0;
trigger.ttls=0x400000; /* line 22 *///
my_adc=new SpectrumM2i40xxSeries(trigger, 50.0, 1e7);
state_sequent* test_sequence = new state_sequent();
state* test_state = new state(1);
analogin* test_in = new analogin();
test_in->id = 0;
//test_in->sensitivity = 5;
test_in->sample_frequency = 2e7;
test_in->resolution = 14;
test_in->samples = 4096;
test_in->channels = channel_array(15);
test_in->nchannels = test_in->channels.count();
test_in->sensitivity = new double[4];
test_in->sensitivity[0] = 10.0;
test_in->sensitivity[1] = 10.0;
test_in->sensitivity[2] = 10.0;
test_in->sensitivity[3] = 10.0;
test_in->impedance = new double[4];
test_in->impedance[0] = 50.0;
test_in->impedance[1] = 50.0;
test_in->impedance[2] = 50.0;
test_in->impedance[3] = 50.0;
test_in->offset = new int[4];
test_in->offset[0] = -50;
test_in->offset[1] = 0;
test_in->offset[2] = 0;
test_in->offset[3] = 0;
test_state->push_back(test_in);
test_sequence->push_back(test_state);
my_adc->set_daq(*test_sequence);
adc_results* resultat = dynamic_cast<adc_results*>(my_adc->get_samples(1));
FILE* outputFile = fopen("./out.txt","w");
int j;
for(j = 0; j < 4*resultat->front()->samples; j += 4) {
fprintf(outputFile, "%i\t%i\t%i\t%i\n", resultat->front()->data[j], resultat->front()->data[j+1], resultat->front()->data[j+2], resultat->front()->data[j+3]);
}
fclose(outputFile);
delete my_adc;
} catch(ADC_exception ae) {
fprintf(stderr,"adc: %s\n",ae.c_str());
}
}

View File

@ -0,0 +1,109 @@
#include "GatedData.h"
#include <cstring>
#include <cassert>
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
/*
size of all childrens' data
*/
size_t DataManagementNode::size() const {
if (child==NULL) return n;
size_t size_n=0;
for (const DataManagementNode* i=child; i!=NULL; i=i->next) size_n+=i->size();
return size_n*n;
}
/*
reduce depth recursively
*/
DataManagementNode* DataManagementNode::reduce() {
// simple case: no children
if (child==NULL) return this;
// evaluate all children's nodes and append remaining
DataManagementNode* i=child;
DataManagementNode* last_child=NULL;
while (i->next!=NULL) {
DataManagementNode* reduced_child=i->reduce();
if (reduced_child->n!=0) {
if (last_child==NULL)
child=reduced_child;
else
last_child->next=reduced_child;
last_child=reduced_child;
last_child->parent=this;
last_child->next=NULL;
}
DataManagementNode* next_child=i->next;
// throw away unused one
if (reduced_child->n==0 || i!=reduced_child) {
i->parent=NULL;
i->next=NULL;
delete i;
}
i=next_child;
}
// now look at the remaining list
if (child==NULL) {
// return empty node
n=0;
}
else
// reduce depth by one
if (child->next==NULL || child->child!=NULL) {
n*=child->n;
DataManagementNode* superficial=child;
child=child->child;
superficial->parent=NULL;
delete superficial;
}
return this;
}
void DataManagementNode::print(FILE* f,size_t indent) {
char* indent_space=new char[indent+1];
memset(indent_space,' ',indent);
indent_space[indent]=0;
if (child==NULL)
fprintf(f,"%s%" SIZETPRINTFLETTER " samples\n",indent_space, n);
else {
fprintf(f,"%s%" SIZETPRINTFLETTER ":\n",indent_space, n);
child->print(f,indent+6);
}
delete indent_space;
if (next!=NULL)
next->print(f,indent);
}
DataManagementNode::DataManagementNode(const DataManagementNode& orig) {
parent=NULL;
if (orig.child!=NULL) {
child=new DataManagementNode(*orig.child);
for (DataManagementNode* i=child; i!=NULL; i=i->next) i->parent=this;
}
if (orig.next!=NULL) next=new DataManagementNode(*orig.next);
n=orig.n;
}
/**
recursive destruction
*/
DataManagementNode::~DataManagementNode() {
assert(parent==NULL);
assert(next==NULL);
while (child!=NULL) {
DataManagementNode* this_one=child;
child=child->next;
this_one->parent=NULL;
this_one->next=NULL;
delete this_one;
}
}

View File

@ -0,0 +1,50 @@
#include <cstddef>
#include <cstdio>
class DataManagementNode;
/**
manages nested ADC data structure
*/
class DataManagementNode {
public:
/// parent node, top node has NULL
DataManagementNode* parent;
/// reference to nested loops
DataManagementNode* child;
/// next node in sequences
DataManagementNode* next;
/**
if the child is NULL, n is the number of real samples
otherwise repeatition number of substructure
*/
size_t n;
/**
default constructor of NOP node
*/
DataManagementNode(class DataManagementNode* par=NULL): parent(par), child(NULL), next(NULL), n(0) {}
/**
(deep) copy constructor
*/
DataManagementNode(const DataManagementNode& orig);
/**
size of all childrens' data
*/
size_t size() const;
/**
reduce depth recursively
*/
DataManagementNode* reduce();
void print(FILE* f,size_t indent=0);
/**
recursive destruction, call only for root
*/
~DataManagementNode();
};

View File

@ -0,0 +1,42 @@
CPPFLAGS=-Iinclude -I../..
CXXFLAGS=-Wshadow -Wall -pedantic -O0
AR=ar
SPC_HEADERS = include/spcerr.h include/regs.h include/dlltyp.h include/spcioctl.inc
SPC_ZIP = ../Spectrum-M2i40xxSeries/drv_spcm_linux_drv_v214b5633.zip
all: clean $(SPC_HEADERS) patch Spectrum-MI40xxSeries.a hw_test_int hw_test_ext
$(SPC_HEADERS): $(SPC_ZIP)
unzip -u $< "*.h" "*.txt" "*.inc" -d include/
patch:
patch -N -p0 < ftbfs_patch.diff
Spectrum-MI40xxSeries.a: Spectrum-MI40xxSeries.o GatedData.o
$(AR) r $@ $^
Spectrum-MI40xxSeries.o: Spectrum-MI40xxSeries.cpp Spectrum-MI40xxSeries.h GatedData.h include/spcerr.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
GatedData.o: GatedData.cpp GatedData.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
test.o: test.cpp Spectrum-MI40xxSeries.h include/spcerr.h
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
test: test.o Spectrum-MI40xxSeries.o ../../core/core.a
$(CXX) -o $@ -I../.. ../ADC.h $^ -lpthread -lm
hw_test_int: hw_test_intclock.cpp
$(CXX) -o $@ -Iinclude $^
hw_test_ext: hw_test_extclock.cpp
$(CXX) -o $@ -Iinclude $^
../../core/core.a:
$(MAKE) -C ../../core core.a
clean:
rm -f *.o *.a *~ test *.core hw_test_ext hw_test_int
rm -rf include

View File

@ -0,0 +1,971 @@
#include <cmath>
#include <stack>
#include <cerrno>
#include <cstring>
#include "pthread.h"
#include "core/core.h"
#include "core/stopwatch.h"
#include "core/result.h"
#include "core/xml_states.h"
#include "Spectrum-MI40xxSeries.h"
#ifndef SPC_DEBUG
# define SPC_DEBUG 0
#endif
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
void SpectrumMI40xxSeries::Configuration::print(FILE* f) {
fprintf(f, "Spectrum MI40xx Series Configuration:\n");
fprintf(f, " Sample Frequency %g Hz, Acquisition Timeout %g\n",samplefreq, timeout);
if (data_structure!=NULL)
data_structure->print(f,2);
else
fprintf(f, " No Data to acquire\n");
for (int i=0; i < lSetChannels; i++) {
fprintf(f, " Channel %i: Impedance %f Ohm, Sensitivity %f V, Coupling %s\n", i, impedance[i], sensitivity[i], (coupling==0)?"DC":"AC");
}
}
SpectrumMI40xxSeries::SpectrumMI40xxSeries(const ttlout& t_line, float impedance, int ext_reference_clock) {
// print neat string to inform the user
fprintf(stderr, "Initializing ADC card\n");
// to be configured
device_id=0;
trigger_line=t_line;
default_settings.qwSetChEnableMap = channel_array(ADC_MI_DEFAULT_CHANNELS);
default_settings.lSetChannels = default_settings.qwSetChEnableMap.count();
default_settings.impedance = new double[default_settings.lSetChannels];
default_settings.sensitivity = new double[default_settings.lSetChannels];
default_settings.offset = new int[default_settings.lSetChannels];
for(int i = 0; i < default_settings.lSetChannels; i++) {
default_settings.impedance[i] = ADC_MI_DEFAULT_IMPEDANCE;
default_settings.sensitivity[i] = ADC_MI_DEFAULT_SENSITIVITY; // Set sensitivity in Volts to the default (maximum) value
default_settings.offset[i] = ADC_MI_DEFAULT_OFFSET; // Set offsets in % of sensitivity to default (0)
}
default_settings.ext_reference_clock=ext_reference_clock; // Hz
effective_settings=NULL;
# if defined __linux__
// ----- open driver -----
deviceno = open ("/dev/spc0", O_RDWR);
if (deviceno == -1) {
std::string error_message="could not open /dev/spc0";
if (errno==0) throw SpectrumMI40xxSeries_error( error_message+" (unknown error)");
else throw SpectrumMI40xxSeries_error( error_message+" ("+strerror(errno)+")");
}
# endif
# if defined __CYGWIN__
// open library dll
spectrum_driver_dll = LoadLibrary("spectrum.dll");
if (spectrum_driver_dll==NULL)
throw SpectrumMI40xxSeries_error("could not open driver dll");
deviceno=0;
// load driver functions
# define load_spectrum_function(name) name = ( name##_type)GetProcAddress(spectrum_driver_dll, #name); \
if (name == NULL) { FreeLibrary(spectrum_driver_dll); \
throw SpectrumMI40xxSeries_error("failed to load function" #name "" ); \
}
load_spectrum_function(SpcInitPCIBoards);
load_spectrum_function(SpcGetParam);
load_spectrum_function(SpcSetParam);
load_spectrum_function(SpcGetData);
// find spectrum boards
boardcount=0;
SpcInitPCIBoards(&boardcount,&PCIVersion);
if (boardcount<=0) {
FreeLibrary(spectrum_driver_dll);
throw SpectrumMI40xxSeries_error("no spectrum boards found");
}
# endif
/* calculate the sample number that can be saved without fifo */
int memory_size=0;
SpcGetParam(deviceno,SPC_PCIMEMSIZE,&memory_size);
fifo_minimal_size=memory_size/(sizeof(short int)*2);
/* get the device type */
SpcGetParam(deviceno, SPC_PCITYP, &cardType);
if (cardType != TYP_MI4020 && cardType != TYP_MI4021 && cardType != TYP_MI4022 && cardType != TYP_MI4030 && cardType != TYP_MI4031 && cardType != TYP_MI4032) {
throw SpectrumMI40xxSeries_error("Board type not supported");
}
/* make a check, whether spectrum board is running */
int actual_status=0;
SpcGetParam(deviceno, SPC_STATUS, &actual_status);
if (actual_status!=SPC_READY) {
fprintf(stderr, "Warning: Spectrum board was is running before reset.\n");
}
#if SPC_DEBUG
fprintf(stderr,"External reference clock: %i\n",default_settings.ext_reference_clock);
#endif
int ErrorOccurred=1;
SpcSetParam(deviceno, SPC_COMMAND, SPC_RESET)==ERR_OK && // reset device first
SpcSetParam (deviceno, SPC_GATE, 1)==ERR_OK && // Gated Triggering
SpcSetParam (deviceno, SPC_PLL_ENABLE, 1)==ERR_OK && // Internal PLL enabled for clock
SpcSetParam (deviceno, SPC_CLOCKOUT, 0)==ERR_OK && // No clock output
SpcSetParam (deviceno, SPC_REFERENCECLOCK, default_settings.ext_reference_clock)==ERR_OK && // external reference clock
SpcSetParam (deviceno, SPC_EXTERNALCLOCK, 0)==ERR_OK && // but no external sample clock
SpcSetParam (deviceno, SPC_CLOCK50OHM, 0)==ERR_OK && // clock input NOT with 50Ohm impedance
SpcSetParam (deviceno, SPC_TRIGGERMODE, TM_TTLPOS)==ERR_OK && // ttl trigger is used
SpcSetParam (deviceno, SPC_TRIGGEROUT, 0)==ERR_OK && // No trigger output
SpcSetParam (deviceno, SPC_TRIGGER50OHM, 0)==ERR_OK && // Trigger to 1MOhm, necessary for steep slopes
(ErrorOccurred=0); // done, really no error occurred
// ----- driver error: request error and end program -----
if (ErrorOccurred!=0) {
int32 lErrorCode, lErrorReg, lErrorValue;
SpcGetParam (deviceno, SPC_LASTERRORCODE, &lErrorCode);
SpcGetParam (deviceno, SPC_LASTERRORREG, &lErrorReg);
SpcGetParam (deviceno, SPC_LASTERRORVALUE, &lErrorValue);
char error_message[256];
snprintf(error_message, sizeof(error_message),"Configuration error %d in register %d at value %d", lErrorCode, lErrorReg, lErrorValue);
throw SpectrumMI40xxSeries_error(error_message);
}
/* print lines with useful information: */
fprintf(stderr, "Spectrum MI40xx series board with %d byte memory\n", memory_size);
fprintf(stderr,
" impedance set to %g Ohm\n expecting trigger on id=%d ttls=0x%lx\n external clock frequency set to %g Hz\n",
impedance,
t_line.id,
t_line.ttls.to_ulong(),
(float)ext_reference_clock );
}
void SpectrumMI40xxSeries::sample_after_external_trigger(double rate, size_t samples, double sensitivity, size_t resolution) {
throw SpectrumMI40xxSeries_error("SpectrumMI40xxSeries::sample_after_external_trigger is not jet implemented");
}
void SpectrumMI40xxSeries::collect_config_recursive(state_sequent& exp, SpectrumMI40xxSeries::Configuration& settings) {
/* start with dummy node */
DataManagementNode* new_branch=new DataManagementNode(NULL);
DataManagementNode* where_to_append=new_branch;
double parent_timeout=settings.timeout;
settings.timeout=0.0;
/* loop over all states and sequences */
for (state_sequent::iterator i=exp.begin(); i!=exp.end(); ++i) {
state* a_state=dynamic_cast<state*>(*i);
if (a_state==NULL)
throw SpectrumMI40xxSeries_error("state_atom not expected in sate_sequent");
if (dynamic_cast<state_parallel*>(*i)!=NULL)
throw SpectrumMI40xxSeries_error("state_parallel handling is not jet implemented");
state_sequent* a_sequence=dynamic_cast<state_sequent*>(a_state);
if (a_sequence!=NULL) {
// found a sequence
DataManagementNode* tmp_structure=settings.data_structure;
settings.data_structure=where_to_append;
collect_config_recursive(*a_sequence, settings);
settings.data_structure=tmp_structure;
} /* end working on sequence */
else {
// found a state, not a sequence
settings.timeout+=a_state->length;
#if SPC_DEBUG
fprintf(stderr,"SETTINGS %e %e\n",settings.timeout,a_state->length);
#endif
// collect analogin sections in state
std::list<analogin*> inputs;
/* loop over all device definitions in a state */
state::iterator j=a_state->begin();
while (j!=a_state->end()) {
analogin* input=dynamic_cast<analogin*>(*j);
if (input!=NULL && input->id==device_id) {
/* collect appropiate analogin sections, forget others */
if (input->samples<=0 || input->sample_frequency<=0)
delete input;
else
inputs.push_back(input);
j=a_state->erase(j);
} else
++j;
}
if (!inputs.empty()) {
/* evaluate the found analogin definitions */
if (inputs.size()>1) {
while (!inputs.empty()) {
delete inputs.front();
inputs.pop_front();
}
throw ADC_exception("can not handle more than one analogin section per state");
}
/* save sampling frequency */
if (settings.samplefreq<=0)
settings.samplefreq=inputs.front()->sample_frequency;
else if (settings.samplefreq != inputs.front()->sample_frequency) {
while (!inputs.empty()) {
delete inputs.front();
inputs.pop_front();
}
throw ADC_exception("Sorry, but gated sampling requires same sampling frequency in all analogin sections");
}
/* save channel mask and number of channels */
if (settings.lSetChannels > 0 && settings.qwSetChEnableMap.to_ulong() > 0) {
if (settings.qwSetChEnableMap != inputs.front()->channels) {
fprintf(stderr, "Warning! different channels enabled in input %lu and in config %lu, setting to default \n",
settings.qwSetChEnableMap.to_ulong(),
inputs.front()->channels.to_ulong());
settings.qwSetChEnableMap = channel_array(ADC_MI_DEFAULT_CHANNELS);
settings.lSetChannels = settings.qwSetChEnableMap.count();
}
} else {
settings.qwSetChEnableMap = inputs.front()->channels;
settings.lSetChannels = inputs.front()->nchannels;
}
/* save sensitivity */
if (settings.sensitivity != NULL) { // if sensitivity is set, make sure it's valid (i.e. the same for all inputs)
for (int k = 0; k < inputs.front()->nchannels; k++) {
if (settings.sensitivity[k] != inputs.front()->sensitivity[k]) {
fprintf(stderr, "Warning! different sensitivity specified (here %f, elsewhere %f), choosing higher voltage\n",
settings.sensitivity[k],
inputs.front()->sensitivity[k]);
if (settings.sensitivity[k] < inputs.front()->sensitivity[k]) {
settings.sensitivity[k] = inputs.front()->sensitivity[k];
}
}
}
} else {
settings.sensitivity = inputs.front()->sensitivity;
}
// check if sensitivity is valid
for (int k = 0; k < inputs.front()->nchannels; k++) {
bool sensAllowed = false;
for (int l = 0; l < ADC_MI_ALLOWED_SENSITIVITY_LENGTH; l++) {
if (settings.sensitivity[k] == ADC_MI_ALLOWED_SENSITIVITY[l] ) {
sensAllowed = true;
break;
}
}
if (!sensAllowed) {
fprintf(stderr, "Warning! Illegal sensitivity specified for channel %i: %f", k, inputs.front()->sensitivity[k]);
settings.sensitivity[k] = ADC_MI_DEFAULT_SENSITIVITY;
}
}
/* save impedance */
if (settings.impedance != NULL) {
for (int k = 0; k < inputs.front()->nchannels; k++) {
if (settings.impedance[k] != inputs.front()->impedance[k]) {
fprintf(stderr, "Warning! different impedance specified (here %f, elsewhere %f), setting to default\n",
settings.impedance[k],
inputs.front()->impedance[k]);
settings.impedance[k] = ADC_MI_DEFAULT_IMPEDANCE;
}
if (settings.impedance[k] != ADC_MI_DEFAULT_IMPEDANCE && settings.impedance[k] != ADC_MI_ALLOWED_IMPEDANCE) {
fprintf(stderr, "Warning! Illegal impedance specified for channel %i: %f",k, inputs.front()->impedance[k]);
settings.offset[k] = 0;
}
}
} else {
settings.impedance = inputs.front()->impedance;
}
/* save offsets */
if (settings.offset != NULL) {
for (int k = 0; k < inputs.front()->nchannels; k++) {
if (settings.offset[k] != inputs.front()->offset[k]) {
fprintf(stderr, "Warning! different impedance specified (here %i, elsewhere %i), setting to default\n",
settings.offset[k],
inputs.front()->offset[k]);
settings.offset[k] = ADC_MI_DEFAULT_OFFSET;
}
if (inputs.front()->offset[k] > 100 || inputs.front()->offset[k] < -100) {
fprintf(stderr, "Warning! Illegal offset specified for channel %i: %i", k, inputs.front()->offset[k]);
settings.offset[k] = 0;
}
}
} else {
settings.offset = inputs.front()->offset;
}
if (inputs.front()->samples%4 != 0) {
throw ADC_exception("Number of samples must be a multiple of four");
}
// calculate the time required
double delayed_gating_time=0.0;
// the gating time has an offset, which was found to be 1.5 dwelltimes for <2.5MHz and 4.5 dwelltimes for >=2.5MHz
double gating_time;
#if SPC_DEBUG
fprintf(stderr, "Channels: %lu\n", settings.qwSetChEnableMap.to_ulong());
#endif
/* check if channel mask is legal for the card */
if (this->IsChannelMaskLegal(inputs.front()->channels.to_ulong())) {
settings.qwSetChEnableMap = inputs.front()->channels;
settings.lSetChannels = inputs.front()->nchannels;
} else {
throw SpectrumMI40xxSeries_error("Selected channels combination not allowed for this card type");
}
/* apply proper timing */
if ( settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL1) || settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL1|CHANNEL2|CHANNEL3) ) {
#if SPC_DEBUG
fprintf(stderr, "Default Channels\n");
#endif
if (settings.samplefreq<2.5e6) {
// if sampling rate is <2.5MHz, there is another data handling mode,
// see MI4021 manual page 79: "Accquisition Delay: -6 Samples"
// it might be necessary to add 0.1 dwelltime to shift the sampling start a little more...
// edit by Stefan Reutter @2013-06, it seems that the MI4021 cards actually have a second
// threshold at 500 kHz that is not mentioned in the manual.
// this can be tested by disabling the if below and switching over the threshold
gating_time=(inputs.front()->samples)/settings.samplefreq;
if (settings.samplefreq >= 5e5) {
delayed_gating_time=ceil(1e8*6.0/settings.samplefreq)/1e8;
} else {
delayed_gating_time=0.0;
}
} else {
gating_time=(inputs.front()->samples+3)/settings.samplefreq;
delayed_gating_time=0.0;
}
}
// disabled the more exotic channel setup as it is untested with the updated timings and probably not used anyways
/* else if (settings.qwSetChEnableMap.to_ulong()==CHANNEL0 || settings.qwSetChEnableMap.to_ulong()==(CHANNEL0|CHANNEL2) ) {
#if SPC_DEBUG
fprintf(stderr, "Weird Channels\n");
#endif
if (settings.samplefreq<5e6) {
gating_time=(inputs.front()->samples+1.5)/settings.samplefreq;
delayed_gating_time=ceil(1e8*6.0/settings.samplefreq)/1e8;
} else {
gating_time=(inputs.front()->samples+4.5)/settings.samplefreq;
delayed_gating_time=-ceil(1e8*7.0/settings.samplefreq)/1e8;
} */else {
throw SpectrumMI40xxSeries_error("Selected channels combination not allowed");
}
gating_time=ceil(1e8*gating_time)/1e8;
double time_required=delayed_gating_time+gating_time;
// check time requirements
if (a_state->length < gating_time) {
char parameter_info[512];
snprintf(parameter_info,sizeof(parameter_info),
"(%" SIZETPRINTFLETTER " samples, %g samplerate, %e time required, %e state time)",
inputs.front()->samples,
settings.samplefreq,
time_required,
a_state->length);
#if SPC_DEBUG
fprintf(stderr, "state is shorter than acquisition time %e time required, %e state time\n", gating_time, a_state->length);
#endif
// update the state length if it's shorter than the gate. this is usually due to rounding to 10 ns for the pulseblaster
if (ceil(1e8*a_state->length)/1e8 < time_required) {
throw ADC_exception(std::string("state is shorter than acquisition time")+parameter_info);
} else {
a_state->length = time_required;
}
}
// if necessary, add the gating pulse delay...
if (delayed_gating_time>0.0) {
state* delayed_gating_state=new state(*a_state);
delayed_gating_state->length=delayed_gating_time;
// insert before me
exp.insert(i,(state_atom*)delayed_gating_state);
} else if (delayed_gating_time < 0.0) {
/*
For +samples delays
1. get the previous state
2. if the length is not long enough (6*dw) add the state before
3. split the state(s) so that we have the gating on 6*dw before the actual recording
*/
double rest_length = delayed_gating_time;
state_sequent::iterator i_prev;
state* prev_state;
i_prev = i;
do {
i_prev--;
prev_state = dynamic_cast<state*>(*(i_prev));
rest_length -= prev_state->length;
fprintf(stderr, "DW rest_length: %g\n", rest_length);
fprintf(stderr, "DW state_length: %g\n", prev_state->length);
if (rest_length >= 0)
prev_state->push_back(trigger_line.copy_new()); // add trigger to this state
else { // split final state
state* prev_state_1 = prev_state->copy_flat(); //create copy of current state
prev_state_1->length += rest_length; // adjust 1st part length
exp.insert(i_prev,(state_atom*) prev_state_1); // insert AFTER prev_state
prev_state->length = -rest_length; // adjust 2nd part length
prev_state->push_back(trigger_line.copy_new()); // add trigger to 2nd part
break;
}
} while (i_prev!=exp.begin() || rest_length > 0.0);
}
# if SPC_DEBUG
fprintf(stderr, "sequence after pre_trigger correction:\n");
xml_state_writer().write_states(stderr, exp);
# endif
// adapt the pulse program for gated sampling
if (a_state->length == gating_time) {
// state has proper length
a_state->push_back(trigger_line.copy_new());
} else {
# if SPC_DEBUG
fprintf(stderr, "state too long, length %e, time required %e\n", a_state->length, time_required);
# endif
// state is too long...
// create new one with proper time and gated sampling pulse
state* gated_sampling_pulse=new state(*a_state);
gated_sampling_pulse->length=gating_time;
gated_sampling_pulse->push_back(trigger_line.copy_new());
// insert gate pulse state before remaining (original) state
exp.insert(i,(state_atom*)gated_sampling_pulse);
// shorten this state
a_state->length-=time_required;
}
# if SPC_DEBUG
fprintf(stderr, "sequence after correcting trigger state:\n");
xml_state_writer().write_states(stderr, exp);
# endif
/* save sampleno */
DataManagementNode* new_one=new DataManagementNode(new_branch);
new_one->n=inputs.front()->samples;
new_one->child=NULL;
new_one->next=where_to_append->next;
where_to_append->next=new_one;
where_to_append=new_one;
while (!inputs.empty()) {delete inputs.front(); inputs.pop_front();}
} /* !inputs.empty() */
} /* end working on state */
} /* i */
/* something happened? */
if (new_branch->next!=NULL) {
/* make dummy node to a loop */
new_branch->n=exp.repeat;
new_branch->child=new_branch->next;
/* if possible, append it */
if (settings.data_structure!=NULL) {
new_branch->parent=settings.data_structure->parent;
new_branch->next=settings.data_structure->next;
settings.data_structure->next=new_branch;
}
else {
new_branch->parent=NULL;
new_branch->next=NULL;
settings.data_structure=new_branch;
}
}
else
delete new_branch;
settings.timeout*=exp.repeat;
settings.timeout+=parent_timeout;
#ifdef SPC_DEBUG
fprintf(stderr,"setting.timout %g\n",settings.timeout);
#endif
return;
}
static void* SpectrumMI40xxSeries_TimeoutThread(void* data) {
int *ret = new int;
*ret = ((SpectrumMI40xxSeries*)data)->TimeoutThread();
return (void*) ret;
}
void SpectrumMI40xxSeries::set_daq(state & exp) {
// cleanup?!
if (!fifobuffers.empty()) {
fprintf(stderr, "normally there should be a cleanup at the end of acquisition\n");
for (std::vector<short int*>::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i);
fifobuffers.clear();
}
/* what could be done: PCIBIT_GATE, PCIBIT_MULTI */
int features;
SpcGetParam(deviceno,SPC_PCIFEATURES,&features);
state_sequent* exp_sequence=dynamic_cast<state_sequent*>(&exp);
if (exp_sequence==NULL)
throw ADC_exception("Spectrum-MI40xxSeries::set_daq only working on sequences");
# if SPC_DEBUG
fprintf(stderr, "working on sequence:\n");
xml_state_writer().write_states(stderr, *exp_sequence);
# endif
/* find out what to do */
Configuration* conf=new Configuration();
collect_config_recursive(*exp_sequence, *conf);
if (conf->samplefreq<=0) conf->samplefreq=default_settings.samplefreq;
if (conf->impedance == NULL) conf->impedance = default_settings.impedance;
if (conf->sensitivity == NULL) conf->sensitivity = default_settings.sensitivity;
if (conf->offset == NULL) conf->offset = default_settings.offset;
if (conf->lSetChannels == 0) {
conf->lSetChannels = default_settings.lSetChannels;
conf->qwSetChEnableMap = default_settings.qwSetChEnableMap;
}
size_t sampleno=(conf->data_structure==NULL)?0:conf->data_structure->size();
/* nothing to do! */
if (sampleno==0) {
delete conf;
effective_settings=NULL;
return;
}
if (sampleno<16 || sampleno%16!=0) {
delete conf;
throw SpectrumMI40xxSeries_error("Total number of samples must be multiple of 16 and at least 16");
}
effective_settings=conf;
#if SPC_DEBUG
/* settings for this experiment */
conf->print(stderr);
#endif
/* make a check, whether spectrum board is running */
int actual_status=0;
SpcGetParam (deviceno, SPC_STATUS, &actual_status);
if (actual_status!=SPC_READY) {
fprintf(stderr, "Warning: Spectrum board was/is running before starting data aquisition.\n");
SpcSetParam (deviceno, SPC_COMMAND, SPC_STOP); // stop the board
}
/* and the dirty things there...
----- setup board for recording -----
*/
for (unsigned int j=0; j<(unsigned int)effective_settings->lSetChannels; j++) {
SpcSetParam (deviceno, SPC_AMP0 + 100*j, (int)floor(effective_settings->sensitivity[j]*1000)); // +/- 5V input range
SpcSetParam (deviceno, SPC_50OHM0 + 100*j, ((effective_settings->impedance[j]==50.0)?1:0)); // 1 = 50 Ohm input impedance, 0 = 1MOhm input impedance
SpcSetParam (deviceno, SPC_OFFS0 + 100*j, effective_settings->offset[j]); // set offset to zero
}
SpcSetParam (deviceno, SPC_CHENABLE, effective_settings->qwSetChEnableMap.to_ulong()); // Enable channels for recording
int activated_channels;
SpcGetParam (deviceno, SPC_CHENABLE, &activated_channels);
SpcSetParam (deviceno, SPC_SAMPLERATE, (int)floor(effective_settings->samplefreq)); // Samplerate
// the MI4021 card doesn't accept all sampling frequency settings. check if the sampling rate is set correctly. if not, throw an exception
int setSamplingRate = 0;
SpcGetParam (deviceno, SPC_SAMPLERATE, &setSamplingRate);
if (setSamplingRate != (int)floor(effective_settings->samplefreq)) {
char parameter_info[16];
snprintf(parameter_info,sizeof(parameter_info), "%d", setSamplingRate);
throw ADC_exception(std::string("DAC sampling rate not available. Try setting to: ")+parameter_info);
}
// decide for aquisition mode and start it
int16 nErr=ERR_OK;
if (sampleno<fifo_minimal_size) {
#if SPC_DEBUG
fprintf(stderr, "expecting %" SIZETPRINTFLETTER " samples in normal mode\n",sampleno);
#endif
SpcSetParam (deviceno, SPC_MEMSIZE, sampleno); // Memory size
// ----- start the board -----
nErr = SpcSetParam (deviceno, SPC_COMMAND, SPC_START); // start the board
} else {
#if SPC_DEBUG
fprintf(stderr, "expecting %" SIZETPRINTFLETTER " samples in fifo mode\n",sampleno);
#endif
// todo: fifo should really write directly to recursive gated sampling structures
fifo_adc_data=(short int*)malloc(sampleno*sizeof(short int)*2);
if (fifo_adc_data==NULL) {
throw SpectrumMI40xxSeries_error("could not allocate adc data memory for fifo recording");
}
// ToDo: do some magic calculations
fifobufferno=16;
fifobufferlen=1<<20;
#if SPC_DEBUG
fprintf(stderr, "configuring for %" SIZETPRINTFLETTER " buffers, each of size %" SIZETPRINTFLETTER "\n", fifobufferno, fifobufferlen);
#endif
SpcSetParam(deviceno,SPC_FIFO_BUFFERS,fifobufferno);
SpcSetParam(deviceno,SPC_FIFO_BUFLEN,fifobufferlen);
// allocate FIFO buffers
fifobuffers.resize(fifobufferno,(short int*)NULL);
for (size_t i=0; i!=fifobufferno; ++i) {
void* newbuffer;
#if SPC_DEBUG
fprintf(stderr, "allocating fifobuffer %" SIZETPRINTFLETTER "...", i);
#endif
newbuffer=malloc(fifobufferlen);
if (newbuffer==NULL) {
// free the buffers, if there is not enough memory
for (size_t j=0; j!=i; ++j) free(fifobuffers[j]);
fifobuffers.clear();
throw SpectrumMI40xxSeries_error("could not allocate buffers for fifo mode");
}
#if SPC_DEBUG
fprintf(stderr, "and registering with ADC driver....");
#endif
fifobuffers[i]=(short int*)newbuffer;
// todo: check for errors
#ifndef _LINUX64
SpcSetParam (deviceno, SPC_FIFO_BUFADR0+i, (int32) newbuffer);
#else
// 64 bit Linux needs SetAdr function, 32 bit linux can also use this if driver build > 1093
SpcSetAdr (deviceno, SPC_FIFO_BUFADR0+i, newbuffer);
#endif
#if SPC_DEBUG
fprintf(stderr, "success.\n");
#endif
}
#if SPC_DEBUG
fprintf(stderr, "starting fifo thread\n");
#endif
// to do: append a new state to overcome problems with fifo...
// need another thread, that collects the data
pthread_attr_init(&timeout_pt_attrs);
pthread_create(&timeout_pt, &timeout_pt_attrs, SpectrumMI40xxSeries_TimeoutThread,(void*)this);
}
// ----- driver error: request error and end program -----
if (nErr != ERR_OK) {
if (effective_settings!=NULL) delete effective_settings;
effective_settings=NULL;
for (std::vector<short int*>::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i);
fifobuffers.clear();
// create an error message
int32 lErrorCode, lErrorReg, lErrorValue;
SpcGetParam (deviceno, SPC_LASTERRORCODE, &lErrorCode);
SpcGetParam (deviceno, SPC_LASTERRORREG, &lErrorReg);
SpcGetParam (deviceno, SPC_LASTERRORVALUE, &lErrorValue);
char error_message[256];
snprintf(error_message, sizeof(error_message),"Configuration error %d in register %d at value %d", lErrorCode, lErrorReg, lErrorValue);
throw SpectrumMI40xxSeries_error(error_message);
}
}
double SpectrumMI40xxSeries::get_sample_clock_frequency() const {
if (effective_settings==NULL ||
effective_settings->samplefreq<=0 ||
effective_settings->data_structure==NULL ||
effective_settings->data_structure->size()==0)
return 0;
return effective_settings->samplefreq;
}
int SpectrumMI40xxSeries::TimeoutThread() {
// now collect data
assert(effective_settings!=NULL);
size_t sampleno=effective_settings->data_structure->size();
size_t buff_number_expected=(sampleno*2*sizeof(short int)+fifobufferlen-1)/fifobufferlen;
SpcSetParam(deviceno, SPC_FIFO_BUFMAXCNT, buff_number_expected);
size_t buff_number=0;
short int* buff_pointer=fifo_adc_data;
// start fifo aquisition
#if SPC_DEBUG
fprintf(stderr, "SPC_FIFOSTART\n");
#endif
SpcSetParam(deviceno, SPC_COMMAND, SPC_FIFOSTART);
do {
// wait for the buffers
pthread_testcancel();
#if SPC_DEBUG
fprintf(stderr, "reading buffer no %" SIZETPRINTFLETTER "/%" SIZETPRINTFLETTER "\n", buff_number+1, buff_number_expected);
#endif
if (buff_number+1==buff_number_expected) {
// the last one is special, copy only expected bytes
memcpy(buff_pointer, fifobuffers[buff_number%(fifobuffers.size())], sampleno*2*sizeof(short int)-fifobufferlen*buff_number);
break;
}
else
buff_pointer=(short int*)mempcpy(buff_pointer, fifobuffers[buff_number%(fifobuffers.size())], fifobufferlen);
SpcSetParam(deviceno,SPC_FIFO_BUFREADY, buff_number%(fifobuffers.size()));
buff_number++;
int buff_available;
SpcGetParam(deviceno, SPC_FIFO_BUFCOUNT, &buff_available);
if (((size_t)buff_available)>buff_number) {
// get also the next buffer...
#if SPC_DEBUG
fprintf(stderr, "read %" SIZETPRINTFLETTER " buffers, %d buffers gained by the hardware sofar\n", buff_number, buff_available);
#endif
continue;
}
int adc_status;
#if SPC_DEBUG
fprintf(stderr, "SPC_STATUS\n");
#endif
SpcGetParam (deviceno, SPC_STATUS, &adc_status);
if (adc_status==SPC_READY) {
// this must be a timeout!
//pthread_cancel(timeout_pt);
//pthread_join(timeout_pt,NULL);
//pthread_attr_destroy(&timeout_pt_attrs);
free(fifo_adc_data);
fifo_adc_data=NULL;
throw SpectrumMI40xxSeries_error("timout occured while collecting data");
return 0;
}
#if SPC_DEBUG
fprintf(stderr, "waiting for next fifo buffer\n");
fprintf(stderr, "SPC_FIFOWAIT\n");
#endif
SpcSetParam(deviceno, SPC_COMMAND, SPC_FIFOWAIT);
}
while (buff_number<buff_number_expected);
#if SPC_DEBUG
fprintf(stderr, "SPC_STOP\n");
#endif
SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP);
return 1;
}
short int* SpectrumMI40xxSeries::split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted) {
short nchannels = effective_settings->lSetChannels;
if (structure.child==NULL) {
// simple case: do real work
// todo: Channel selection
short int* datachunk=(short int*)malloc(sizeof(short int)*structure.n*nchannels);
if (datachunk==NULL) {
throw SpectrumMI40xxSeries_error("not enough memory to create results");
}
// todo: error checking
memcpy(datachunk, data, sizeof(short int)*structure.n*nchannels);
data+=structure.n*nchannels;
adc_result* the_result=new adc_result(0, structure.n, datachunk, effective_settings->samplefreq,nchannels);
result_splitted.push_back(the_result);
if (structure.next!=NULL)
data=split_adcdata_recursive(data, *(structure.next), result_splitted);
}
else
for (size_t i=0; i<structure.n; ++i) {
data=split_adcdata_recursive(data, *(structure.child), result_splitted);
}
return data;
}
result* SpectrumMI40xxSeries::get_samples(double _timeout) {
if (core::term_signal!=0) return NULL;
size_t sampleno=(effective_settings==NULL || effective_settings->data_structure==NULL)?0:effective_settings->data_structure->size();
if (sampleno==0) return new adc_result(1,0,NULL);
short nchannels=(effective_settings==NULL)?0:effective_settings->lSetChannels;
if (nchannels==0) throw SpectrumMI40xxSeries_error("No channels specified");
// double timeout=_timeout;
#if SPC_DEBUG
fprintf(stderr,"effective_settings in get_samples: %g\n",effective_settings->timeout);
#endif
// allocate a lot of space
// todo: use GatedSampling buffers directly!
if (fifobuffers.empty()) {
#if SPC_DEBUG
printf("ADC not in FIFO mode\n");
#endif
short int* adc_data=(short int*)malloc(sampleno*sizeof(short int)*nchannels);
if (adc_data==NULL) {
for (std::vector<short int*>::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i);
fifobuffers.clear();
throw SpectrumMI40xxSeries_error("could not allocate adc data memory");
}
// simple version: standard acquisition, wait and get...
#if SPC_DEBUG
fprintf(stderr, "fetching %" SIZETPRINTFLETTER " samples in normal mode (timeout is %g)\n",sampleno,effective_settings->timeout);
#endif
stopwatch adc_timer;
adc_timer.start();
int adc_status, i;
double elapsed_time=0;
/*
The gate-end signal can only be recognized at an eigth samples alignement.
The board can record up to 7 more samples, the timeout has to be allowed to be longer
*/
double post_gate_maxtime=(1e8*7.5/effective_settings->samplefreq)/1e8;
i = 0;
SpcGetParam(deviceno, SPC_STATUS, &adc_status);
/*
Sometimes the timeout is not enough, the reason could be that the timer
starts running before the actual start of the pulse program because the OS is not loading and starting
the pulseblaster immediatly. It is not possible to
synchronize the timers for now.
Thus, we add a generous 1s to the timeout to be on the safe side. One second ought to be enoug for everybody!
*/
while (core::term_signal==0 && adc_status!=SPC_READY && elapsed_time<=(effective_settings->timeout + post_gate_maxtime + 0.5) ) {
timespec sleeptime;
sleeptime.tv_nsec=10*1000*1000; // 10 ms
sleeptime.tv_sec=0;
nanosleep(&sleeptime,NULL);
SpcGetParam(deviceno, SPC_STATUS, &adc_status);
elapsed_time=adc_timer.elapsed();
#if SPC_DEBUG
fprintf(stderr, "(nofifo) loop %i (adc_status, t, t_out, post_gate, core): %i %g %g %g %i\n",i,adc_status, elapsed_time, effective_settings->timeout, post_gate_maxtime, core::term_signal);
#endif
i++;
}
#if SPC_DEBUG
fprintf(stderr, "(nofifo) loop stopped (adc_status, t, t_out, core): %i %g %g %i\n",adc_status, elapsed_time, effective_settings->timeout, core::term_signal);
#endif
/* card ready to transfer samples */
if (adc_status!=SPC_READY) {
free(adc_data);
#if SPC_DEBUG
fprintf(stderr, "ERROR: adc_status not ready (%i) \n",adc_status );
#endif
throw SpectrumMI40xxSeries_error("timeout occured while collecting data");
}
SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP);
if (core::term_signal!=0) {
#if SPC_DEBUG
fprintf(stderr, "received core::term_signal %i\n",core::term_signal);
#endif
free(adc_data);
return NULL;
}
# if defined __linux__
size_t data_length=SpcGetData (deviceno, 0, 0, nchannels * sampleno, 2, (dataptr) adc_data);
# if SPC_DEBUG
fprintf(stderr, "SpcGetData returned %" SIZETPRINTFLETTER " bytes, got %" SIZETPRINTFLETTER " out of %" SIZETPRINTFLETTER " expected samples\n", data_length, data_length/sizeof(short int)/2, sampleno);
# endif
if (nchannels*sizeof(short int)*sampleno!=data_length) {
free(adc_data);
throw SpectrumMI40xxSeries_error("wrong amount of data from adc card");
}
// current position in adc data array
# elif defined __CYGWIN__
int16 read_result=SpcGetData (deviceno, 0, 0, nchannels * sampleno,(void*) adc_data);
# if SPC_DEBUG
fprintf(stderr, "SpcGetData returned %d\n", read_result);
# endif
if (read_result!=0) {
free(adc_data);
throw SpectrumMI40xxSeries_error("wrong amount of data from adc card");
}
# endif
short int* data_position=adc_data;
// produced results
adc_results* the_results=new adc_results(0);
data_position=split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results);
if (data_position==0 || (size_t)(data_position-adc_data)!=(sampleno*nchannels)) {
fprintf(stderr,"something went wrong while spliting data\n");
}
free(adc_data);
delete effective_settings;
effective_settings=NULL;
return the_results;
}
else {
#if SPC_DEBUG
printf("ADC in FIFO mode!\n");
fprintf(stderr, "fetching %" SIZETPRINTFLETTER " samples in fifo mode (timeout is %g)\n",sampleno,effective_settings->timeout);
#endif
// FIFO method
stopwatch timer;
timer.start();
do {
int32 lStatus;
timespec sleeptime;
sleeptime.tv_sec=0;
sleeptime.tv_nsec=100*1000*1000;
nanosleep(&sleeptime,NULL);
//pthread_testcancel();
SpcGetParam (deviceno, SPC_STATUS, &lStatus);
#if SPC_DEBUG
fprintf(stderr, "while loop FIFO (lStatis, timer, effective_timeout): %i %g %g\n",lStatus, timer.elapsed(), effective_settings->timeout );
#endif
if (lStatus == SPC_READY) break;
} while (timer.elapsed()<effective_settings->timeout); //rosi _timeout
SpcSetParam(deviceno, SPC_COMMAND, SPC_STOP);
#if SPC_DEBUG
fprintf(stderr, "waiting for read thread terminating\n");
#endif
/* wait for read thread */
pthread_cancel(timeout_pt);
pthread_join(timeout_pt,NULL);
pthread_attr_destroy(&timeout_pt_attrs);
/* free it's resources */
#if SPC_DEBUG
fprintf(stderr, "freeing fifo data buffers\n");
#endif
for (std::vector<short int*>::iterator i=fifobuffers.begin(); i!=fifobuffers.end(); ++i) free(*i);
fifobuffers.clear();
adc_results* the_results=NULL;
if (fifo_adc_data!=NULL) {
// split data to (small) pieces
short int* data_position=fifo_adc_data;
the_results=new adc_results(0);
data_position=split_adcdata_recursive(data_position, *(effective_settings->data_structure), *the_results);
if (data_position==0 || (size_t)(data_position-fifo_adc_data)!=(sampleno*nchannels)) {
fprintf(stderr,"something went wrong while spliting data from fifo mode\n");
}
free(fifo_adc_data);
fifo_adc_data=NULL;
}
else
fprintf(stderr,"something went wrong while collecting data with fifo mode\n");
delete effective_settings;
effective_settings=NULL;
return the_results;
}
}
/* check whether a channel mask is legal for this board */
bool SpectrumMI40xxSeries::IsChannelMaskLegal(int mask) {
if ( (cardType == TYP_MI4020 || cardType == TYP_MI4030) && mask != CHANNEL0) {
return false;
} else if ( (cardType == TYP_MI4021 || cardType == TYP_MI4031) && mask != (CHANNEL0 | CHANNEL1) && mask != CHANNEL0) {
return false;
} else if ((cardType == TYP_MI4022 || cardType == TYP_MI4032) && mask != (CHANNEL0 | CHANNEL1 | CHANNEL2 | CHANNEL3) && mask != (CHANNEL0 | CHANNEL1) && mask != (CHANNEL0 | CHANNEL2) && mask != CHANNEL0) {
return false;
}
return true;
}
SpectrumMI40xxSeries::~SpectrumMI40xxSeries() {
SpcSetParam (deviceno, SPC_COMMAND, SPC_STOP); // stop the board
# if defined __linux__
close (deviceno);
# elif defined __CYGWIN__
FreeLibrary(spectrum_driver_dll);
# endif
}

View File

@ -0,0 +1,221 @@
#ifndef SPECTRUM_MI40XXSERIES
#define SPECTRUM_MI40XXSERIES
#include <string>
#include <vector>
#include "core/states.h"
#include "core/constants.h"
#include "drivers/ADC.h"
#include "GatedData.h"
#if defined __linux__
// ----- linux includes -----
# include <cstdio>
# include <fcntl.h>
# include <sys/ioctl.h>
# include <unistd.h>
# include "include/dlltyp.h"
# include "include/regs.h"
# include "include/spcerr.h"
#endif
#if defined __CYGWIN__
// ------- for cygwin with windows ----------
# define int16 short int
# define int32 int
# define ptr16 short int*
# include "include/regs.h"
# include "include/spcerr.h"
# include "windows.h"
#endif
#if !(defined __linux__ || defined __CYGWIN__)
# error "sorry, expecting linux or cygwin"
#endif
#ifndef SPC_DEBUG
# define SPC_DEBUG 0
#endif
/**
\defgroup spectrummi40xxadc Spectrum MI-40xx ADC
\ingroup drivers
\brief Driver for the Spectrum MI-40xx Series analog-to-digital converter (acquisition) boards
@{
*/
class SpectrumMI40xxSeries_error: public ADC_exception
{
public:
explicit SpectrumMI40xxSeries_error(const std::string& msg) throw (): ADC_exception(msg) {}
explicit SpectrumMI40xxSeries_error(const char* msg) throw (): ADC_exception(msg) {}
virtual ~SpectrumMI40xxSeries_error() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (SpectrumMI40xxSeries_error): "; }
};
/**
driver for Spectrum MI40 Series acquisition boards
*/
class SpectrumMI40xxSeries: public ADC {
/**
analogin device number
*/
int deviceno;
/**
ttlout triggerline device id
*/
int device_id;
/**
ttlout triggerline ttlmask
*/
ttlout trigger_line;
/**
Type of the card. This is important for the number of allowed channels, etc.
*/
int cardType;
/** stuff concerning fifo acquisition */
size_t fifobufferno;
/** stuff concerning fifo acquisition */
size_t fifobufferlen;
/** stuff concerning fifo acquisition */
size_t fifo_minimal_size;
/** stuff concerning fifo acquisition */
std::vector<short int*> fifobuffers;
/** the extra data thread should store things to that buffer */
short int* fifo_adc_data;
pthread_attr_t timeout_pt_attrs;
pthread_t timeout_pt;
# if defined __linux__
// ----- include the easy ioctl commands from the driver -----
# include "include/spcioctl.inc"
# endif
# if defined __CYGWIN__
HINSTANCE spectrum_driver_dll;
int16 boardcount;
int16 PCIVersion;
# define define_spectrum_function(name, returntype, ... ) typedef returntype (__attribute__((stdcall)) *name##_type)(__VA_ARGS__); name##_type name
define_spectrum_function(SpcSetParam, int16, int16, int32, int32);
define_spectrum_function(SpcGetParam, int16, int16, int32, int32*);
define_spectrum_function(SpcGetData, int16, int16, int16, int32, int32, void*);
define_spectrum_function(SpcInitPCIBoards, int16, int16*, int16*);
# endif
class Configuration {
public:
/* sampling frequency in Hz */
double samplefreq;
/* acquired data description */
DataManagementNode* data_structure;
/* a timeout for acquiring data in s*/
double timeout;
/** configured input impedance in Ohm*/
double* impedance;
/** configured input sensitivity in V*/
double* sensitivity;
/** coupling 0=DC else AC */
int coupling;
/** external reference clock **/
int ext_reference_clock;
/** offsets for each channel in % of sensitivity **/
int* offset;
/** \brief Number of used channels for this run */
int lSetChannels;
/** \brief Bitmap for currently enabled channels */
channel_array qwSetChEnableMap;
/** print data for debug purpose */
void print(FILE* f);
Configuration() {
samplefreq=0;
timeout=0;
impedance=NULL;
sensitivity=NULL;
offset = NULL;
coupling=0;
ext_reference_clock=0;
data_structure=NULL;
lSetChannels=0;
qwSetChEnableMap=0;
}
Configuration(const Configuration& orig) {
samplefreq=orig.samplefreq;
timeout=orig.timeout;
impedance=orig.impedance;
sensitivity=orig.sensitivity;
offset=orig.offset;
coupling=orig.coupling;
ext_reference_clock=orig.ext_reference_clock;
qwSetChEnableMap = orig.qwSetChEnableMap;
lSetChannels = orig.lSetChannels;
if (orig.data_structure==NULL) data_structure=NULL;
else data_structure=new DataManagementNode(*orig.data_structure);
}
~Configuration() {
if (data_structure!=NULL) delete data_structure;
}
};
/**
these settings are copied and modified at need to derive effective settings
*/
Configuration default_settings;
/**
only available with started measurement
*/
Configuration* effective_settings;
short int* split_adcdata_recursive(short int* data, const DataManagementNode& structure, adc_results& result_splitted);
void collect_config_recursive(state_sequent& exp, SpectrumMI40xxSeries::Configuration& settings);
bool IsChannelMaskLegal(int mask);
public:
SpectrumMI40xxSeries(const ttlout& t_line, float impedance=1e6, int ext_reference_clock=(int)100e6);
virtual void sample_after_external_trigger(double rate, size_t samples, double sensitivity=5.0, size_t resolution=14);
virtual void set_daq(state & exp);
/**
for syncronization purposes with sample clock
*/
double get_sample_clock_frequency() const;
/**
get results from transient recorder
timeout: 0.0 return immediately
*/
virtual result* get_samples(double timeout=0.0);
/**
timeout issues during fifo acquisition
*/
int TimeoutThread();
virtual ~SpectrumMI40xxSeries();
};
/**
@}
*/
#endif

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,26 @@
--- include/dlltyp.h 2012-08-06 14:02:18.000000000 +0200
+++ ../../../dlltyp.h 2012-11-13 10:10:23.000000000 +0100
@@ -262,14 +262,15 @@
// ----- Linux -----
#ifdef _LINUX
-# define int8 char
-# define int16 short int
-# define int32 int
-# define int64 long long
-# define uint8 unsigned char
-# define uint16 unsigned short int
-# define uint32 unsigned int
-# define uint64 unsigned long long
+#include <stdint.h>
+typedef int8_t int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
# define dataptr void *
# define ptr8 int8*
# define ptr16 int16*

View File

@ -0,0 +1,135 @@
/*
* simple test program simplified from
* mi40xx.cpp example program (c) Spectrum GmbH
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "dlltyp.h"
#include "regs.h"
#include "spcerr.h"
#include "spcioctl.inc"
// ----- main task -----
int main(int argc, char **argv)
{
int hDrv;
//int16 nCount, nPCIBusVersion
int16 nChannels;
int32 lChEnable, lStatus, lMemsize;
int32 lErrorCode, lErrorReg, lErrorValue;
ptr16 pnData[4];
ptr16 pnTmp;
int32 i, k;
int16 j, nErr;
hDrv = open ("/dev/spc0", O_RDWR);
if (hDrv <= 0)
{
printf ("device not found\n");
return -1;
}
nChannels = 2;
lChEnable = CHANNEL0 | CHANNEL1;
// ----- set memsize for recording ----
lMemsize = 2*1024;
// ----- setup board for recording -----
for (j=0; j<nChannels; j++)
SpcSetParam (hDrv, SPC_AMP0 + 100*j, 5000); // 5 V sensitivity
for (j=0; j<nChannels; j++)
SpcSetParam (hDrv, SPC_50OHM0 + 100*j, 1); // 50 Ohm input impedance
SpcSetParam (hDrv, SPC_CHENABLE, lChEnable); // Enable channels for recording
SpcSetParam (hDrv, SPC_POSTTRIGGER, lMemsize/2); // Trigger event in middle of data
SpcSetParam (hDrv, SPC_MEMSIZE, lMemsize); // Memory size
SpcSetParam (hDrv, SPC_PLL_ENABLE, 1); // Internal PLL enabled for clock
SpcSetParam (hDrv, SPC_EXTERNALCLOCK, 0); // Internal clock used
SpcSetParam (hDrv, SPC_CLOCKOUT, 0); // no clock out
SpcSetParam (hDrv, SPC_REFERENCECLOCK, 50000000); // External reference clock used
SpcSetParam (hDrv, SPC_CLOCK50OHM, 0); // clock impedance NOT 50 Ohm
printf ("Using external internal clock\n");
SpcSetParam (hDrv, SPC_SAMPLERATE, 10000000); // Samplerate: 10 MHz
SpcSetParam (hDrv, SPC_EXTERNOUT, 0); // No clock output
SpcSetParam (hDrv, SPC_TRIGGERMODE, TM_SOFTWARE); // Software trigger is used
SpcSetParam (hDrv, SPC_PULSEWIDTH, 0); // Not used for software mode
SpcSetParam (hDrv, SPC_TRIGGEROUT, 0); // No trigger output
// ----- start the board -----
printf("Starting recording\n");
nErr = SpcSetParam (hDrv, SPC_COMMAND, SPC_START); // start the board
// ----- driver error: request error and end program -----
if (nErr != ERR_OK)
{
SpcGetParam (hDrv, SPC_LASTERRORCODE, &lErrorCode);
SpcGetParam (hDrv, SPC_LASTERRORREG, &lErrorReg);
SpcGetParam (hDrv, SPC_LASTERRORVALUE, &lErrorValue);
printf ("Driver error: %i in register %i at value %i\n", lErrorCode, lErrorReg, lErrorValue);
return -1;
}
// ----- Wait for Status Ready -----
printf("Wating\n");
do
{
SpcGetParam (hDrv, SPC_STATUS, &lStatus);
}
while (lStatus != SPC_READY);
printf ("Board is ready, recording has finished\n");
// ----- allocate memory for data -----
for (i=0; i<nChannels; i++)
pnData[i] = (ptr16) malloc (lMemsize * sizeof(int16));
// ----- data is muxed and must be splitted -----
pnTmp = (ptr16) malloc (lMemsize * nChannels * sizeof(int16));
SpcGetData (hDrv, 0, 0, nChannels * lMemsize, 2, (dataptr) pnTmp);
for (i=0; i<lMemsize; i++)
for (k=0; k<nChannels; k++)
pnData[k][i] = pnTmp[nChannels * i + k];
free (pnTmp);
// ----- output samples -----
printf ("# Data (Integer):\n# Ch0 Ch1\n");
for (i=0; i<lMemsize; i++)
{
for (j=0; j<nChannels; j++)
printf ("% 5i ", pnData[j][i]);
printf ("\n");
}
// ----- free data memory -----
for (i=0; i<nChannels; i++)
free (pnData[i]);
close (hDrv);
return 0;
}

View File

@ -0,0 +1,135 @@
/*
* simple test program simplified from
* mi40xx.cpp example program (c) Spectrum GmbH
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "dlltyp.h"
#include "regs.h"
#include "spcerr.h"
#include "spcioctl.inc"
// ----- main task -----
int main(int argc, char **argv)
{
int hDrv;
//int16 nCount, nPCIBusVersion
int16 nChannels;
int32 lChEnable, lStatus, lMemsize;
int32 lErrorCode, lErrorReg, lErrorValue;
ptr16 pnData[4];
ptr16 pnTmp;
int32 i, k;
int16 j, nErr;
hDrv = open ("/dev/spc0", O_RDWR);
if (hDrv <= 0)
{
printf ("device not found\n");
return -1;
}
nChannels = 2;
lChEnable = CHANNEL0 | CHANNEL1;
// ----- set memsize for recording ----
lMemsize = 2*1024;
// ----- setup board for recording -----
for (j=0; j<nChannels; j++)
SpcSetParam (hDrv, SPC_AMP0 + 100*j, 5000); // 5 V sensitivity
for (j=0; j<nChannels; j++)
SpcSetParam (hDrv, SPC_50OHM0 + 100*j, 1); // 50 Ohm input impedance
SpcSetParam (hDrv, SPC_CHENABLE, lChEnable); // Enable channels for recording
SpcSetParam (hDrv, SPC_POSTTRIGGER, lMemsize/2); // Trigger event in middle of data
SpcSetParam (hDrv, SPC_MEMSIZE, lMemsize); // Memory size
SpcSetParam (hDrv, SPC_PLL_ENABLE, 1); // Internal PLL enabled for clock
SpcSetParam (hDrv, SPC_EXTERNALCLOCK, 0); // Internal clock used
SpcSetParam (hDrv, SPC_CLOCKOUT, 0); // no clock out
SpcSetParam (hDrv, SPC_REFERENCECLOCK, 0); // Internal clock used
SpcSetParam (hDrv, SPC_CLOCK50OHM, 0); // clock 50 Ohm
printf ("Using external internal clock\n");
SpcSetParam (hDrv, SPC_SAMPLERATE, 10000000); // Samplerate: 10 MHz
SpcSetParam (hDrv, SPC_EXTERNOUT, 0); // No clock output
SpcSetParam (hDrv, SPC_TRIGGERMODE, TM_SOFTWARE); // Software trigger is used
SpcSetParam (hDrv, SPC_PULSEWIDTH, 0); // Not used for software mode
SpcSetParam (hDrv, SPC_TRIGGEROUT, 0); // No trigger output
// ----- start the board -----
printf("Starting recording\n");
nErr = SpcSetParam (hDrv, SPC_COMMAND, SPC_START); // start the board
// ----- driver error: request error and end program -----
if (nErr != ERR_OK)
{
SpcGetParam (hDrv, SPC_LASTERRORCODE, &lErrorCode);
SpcGetParam (hDrv, SPC_LASTERRORREG, &lErrorReg);
SpcGetParam (hDrv, SPC_LASTERRORVALUE, &lErrorValue);
printf ("Driver error: %i in register %i at value %i\n", lErrorCode, lErrorReg, lErrorValue);
return -1;
}
// ----- Wait for Status Ready -----
printf("Wating\n");
do
{
SpcGetParam (hDrv, SPC_STATUS, &lStatus);
}
while (lStatus != SPC_READY);
printf ("Board is ready, recording has finished\n");
// ----- allocate memory for data -----
for (i=0; i<nChannels; i++)
pnData[i] = (ptr16) malloc (lMemsize * sizeof(int16));
// ----- data is muxed and must be splitted -----
pnTmp = (ptr16) malloc (lMemsize * nChannels * sizeof(int16));
SpcGetData (hDrv, 0, 0, nChannels * lMemsize, 2, (dataptr) pnTmp);
for (i=0; i<lMemsize; i++)
for (k=0; k<nChannels; k++)
pnData[k][i] = pnTmp[nChannels * i + k];
free (pnTmp);
// ----- output samples -----
printf ("# Data (Integer):\n# Ch0 Ch1\n");
for (i=0; i<lMemsize; i++)
{
for (j=0; j<nChannels; j++)
printf ("% 5i ", pnData[j][i]);
printf ("\n");
}
// ----- free data memory -----
for (i=0; i<nChannels; i++)
free (pnData[i]);
close (hDrv);
return 0;
}

View File

@ -0,0 +1,6 @@
#include "Spectrum-MI40xxSeries.h"
int main() {
SpectrumMI40xxSeries s;
}

View File

@ -0,0 +1,45 @@
# author: Achim Gaedke
# created: February 2005
ifdef KERNELRELEASE
obj-m += pulseblaster.o
else
ifeq ($(shell uname -o), GNU/Linux)
KERNELSRC=/lib/modules/$(shell uname -r)/build
endif
THISDIR=$(shell cd . >/dev/null; pwd)
LIBS=-lm -lxerces-c -lexpat
CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I../..
all: PulseBlasterProgram.o SpinCore-PulseBlaster.o
pulseblaster_test: pulseblaster_test.cpp SpinCore-PulseBlaster.o
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o$@ $^ $(LIBS)
TestCase: TestCase.cpp SpinCore-PulseBlaster.o ../../core/core.a
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -I. -o$@ $^ $(LIBS)
PulseBlasterProgram.o: PulseBlasterProgram.cpp PulseBlasterProgram.h
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o$@ $<
SpinCore-PulseBlaster.o: SpinCore-PulseBlaster.cpp SpinCore-PulseBlaster.h
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o$@ $<
../../core/core.a:
$(MAKE) -C ../../core core.a
pulseblaster.ko:
$(MAKE) -C $(KERNELSRC) SUBDIRS=$(THISDIR)
clean:
rm -f *.o *~ pulseblaster_test
ifeq ($(shell uname -o), GNU/Linux)
$(MAKE) -C $(KERNELSRC) SUBDIRS=$(THISDIR) clean
endif
endif

View File

@ -0,0 +1,322 @@
#include "SpinCore-PulseBlaster.h"
#include "PulseBlasterProgram.h"
#include "core/xml_states.h"
#include <typeinfo>
/* ***************************************************************************************
PulseBlasterCommand
**************************************************************************************** */
PulseBlasterCommand::PulseBlasterCommand() {
ttls=0;
instruction=SpinCorePulseBlaster::CONTINUE;
length=0;
loop_count=0;
program=NULL;
}
PulseBlasterCommand::PulseBlasterCommand(const PulseBlasterCommand& orig) {
instruction=orig.instruction;
length=orig.length;
ttls=orig.ttls;
program=orig.program;
switch(instruction) {
case SpinCorePulseBlaster::LOOP:
case SpinCorePulseBlaster::LONG_DELAY:
loop_count=orig.loop_count;
jump=program->end();
break;
case SpinCorePulseBlaster::BRANCH:
case SpinCorePulseBlaster::END_LOOP:
case SpinCorePulseBlaster::JSR:
jump=orig.jump;
loop_count=0;
break;
default:
loop_count=0;
jump=program->end();
}
}
PulseBlasterCommand::PulseBlasterCommand(PulseBlasterProgram& p) {
ttls=0;
instruction=SpinCorePulseBlaster::CONTINUE;
length=p.minimum_interval;
program=&p;
loop_count=0;
jump=p.end();
}
PulseBlasterCommand::PulseBlasterCommand(PulseBlasterProgram& p, int _ttls, double _length) {
ttls=_ttls;
loop_count=0;
instruction=SpinCorePulseBlaster::CONTINUE;
program=&p;
jump=p.end();
length=(size_t)floor(_length*p.internal_clock_freq+0.5); // graceful rounding
}
/* ***************************************************************************************
PulseBlasterProgram
**************************************************************************************** */
PulseBlasterProgram::PulseBlasterProgram(const PulseBlasterProgram& orig): std::list<PulseBlasterCommand*>() {
// do not copy the list. This must be done by the derived class...
internal_clock_freq=orig.internal_clock_freq;
minimum_interval=orig.minimum_interval;
}
int PulseBlasterProgram::insert_ref(PulseBlasterProgram::iterator target_pos,
PulseBlasterProgram::const_iterator start_copy,
PulseBlasterProgram::const_iterator end_copy) {
insert(target_pos,start_copy,end_copy);
iterator i=target_pos;
for(const_iterator orig_i=end_copy;orig_i!=start_copy;--orig_i) {
if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) {
(**i).jump=i;
advance(i,distance(start_copy,(**orig_i).jump)-distance(start_copy,orig_i));
}
--i;
}
return 0;
}
PulseBlasterProgram::~PulseBlasterProgram() {
while (!empty()) {
if (back()!=NULL) delete back();
pop_back();
}
}
void PulseBlasterProgram::append_sequence(const state& the_states) {
std::list<stackentry> the_stack;
// recieve a work copy with partially enrolled loops and flat structures
state* flat=the_states.copy_flat(0);
push_back(create_command());
iterator dummy_pos=--end();
state_atom* this_tag=flat;
while (1) {
// operate on a sequence
if (typeid(*this_tag)==typeid(state_sequent)) {
//fprintf(stderr,"found sequence\n");
state_sequent* sequence=dynamic_cast<state_sequent*>(this_tag);
// throw away all elements, which were non-states
for(state_sequent::iterator i=sequence->begin(); sequence->end()!=i;) {
if (dynamic_cast<state*>(*i)==0) {
// states are expected, nothing else!
fprintf(stderr, "only states are allowed inside a sequent section!\n");
delete *i;
i=sequence->erase(i);
}
else
// fine
++i;
}
// skip if empty
if (!sequence->empty() && sequence->repeat>0) {
if (sequence->repeat>1<<20) {
// nested loops as workaround
size_t new_count1=1<<20;
size_t new_count2=sequence->repeat/new_count1;
size_t new_count3=sequence->repeat-new_count1*new_count2;
// if (new_count2==1) {}
// if (new_count3==0) {}
state_sequent* new_sequence1=new state_sequent();
new_sequence1->repeat=new_count1;
state_sequent* new_sequence2=new state_sequent(*sequence);
new_sequence2->repeat=new_count2;
new_sequence1->push_back(new_sequence2);
state_sequent* new_sequence3=new state_sequent(*sequence);
new_sequence3->repeat=new_count3;
while (!sequence->empty()) {
delete sequence->front();
sequence->pop_front();
}
sequence->repeat=1;
sequence->push_back(new_sequence1);
sequence->push_back(new_sequence3);
}
// if necessary enroll loop at begin
// prepare sequence
if (sequence->repeat>1) {
while (1) {
state* first_state=NULL;
state_sequent::iterator i=sequence->begin();
while( i!=sequence->end()) {
first_state=dynamic_cast<state*>(*i);
if (NULL!=first_state) break;
++i;
}
if (first_state!=NULL) {
state_sequent* first_sequence=dynamic_cast<state_sequent*>(first_state);
if (first_sequence!=NULL) {
// fprintf(stderr,"enroll begin\n");
size_t repeat=first_sequence->repeat;
if (repeat==0) {
delete *i;
sequence->erase(i);
}
else if (repeat==1) {
sequence->insert(i,first_sequence->begin(),first_sequence->end());
first_sequence->clear();
delete *i;
sequence->erase(i);
}
else {
for (state_sequent::iterator j=first_sequence->begin();j!=first_sequence->end();++j)
sequence->insert(i,(*j)->copy_new());
first_sequence->repeat=repeat-1;
}
// check again...
continue;
}
}
// nothing to be done
break;
}
// if necessary enroll loop at end
while (1) {
state* last_state=NULL;
state_sequent::iterator i=sequence->end();
while(i!=sequence->begin()) {
--i;
last_state=dynamic_cast<state*>(*i);
if (last_state!=NULL) break;
}
if (last_state!=NULL) {
state_sequent* last_sequence=dynamic_cast<state_sequent*>(last_state);
if (last_sequence!=NULL) {
if (last_sequence->repeat==0) {
// forget this sequence
delete *i;
sequence->erase(i);
}
else if (last_sequence->repeat==1) {
// forget the sequence around it
sequence->insert(i,last_sequence->begin(),last_sequence->end());
last_sequence->clear();
delete *i;
sequence->erase(i);
}
else {
// fprintf(stderr,"enroll end\n");
last_sequence->repeat-=1;
i++;
for (state_sequent::iterator j=last_sequence->begin();j!=last_sequence->end();++j)
sequence->insert(i,(*j)->copy_new());
}
// check again
continue;
}
break;
}
}
}
// step down
// push to stack
stackentry new_entry={sequence, sequence->begin(), --end()};
#if 0
if (new_entry.command==the_program->end())
fprintf(stderr,"saved an end iterator\n");
else
fprintf(stderr,"saved pos no %d\n",distance(begin(),new_entry.command));
for (PulseBlasterProgram::iterator i=++begin();i!=end();i++)
i->write_to_file(stderr);
#endif
the_stack.push_back(new_entry);
this_tag=*(the_stack.back().subsequence_position);
continue;
}
}
// operate on a single state
else if (typeid(*this_tag)==typeid(state)) {
//fprintf(stderr,"found state\n");
state* this_state=dynamic_cast<state*>(this_tag);
if (this_state->length*internal_clock_freq>pow(2,32)-1) {
// add a loop to state, to gain longer durations
// numerical stability?
// todo: avaoid unnecessary loop or state
const double loop_state_length=42.949;
size_t number_loops=(size_t)floor(this_state->length/loop_state_length);
double remaining_time=this_state->length-loop_state_length*number_loops;
if (remaining_time*internal_clock_freq<minimum_interval) {
// now, the remaining state might be too short
remaining_time+=loop_state_length;
--number_loops;
}
state_sequent* new_loop=new state_sequent;
new_loop->repeat=number_loops;
this_state->length=loop_state_length;
new_loop->push_back(this_state->copy_new());
this_state->length=remaining_time;
if (the_stack.empty()) {
// in addition to this make a sequence around it
state_sequent* sequence_envelope=new state_sequent;
sequence_envelope->push_back(new_loop);
sequence_envelope->push_back(this_state);
this_state=sequence_envelope;
}
else {
// insert the loop after this state
state_sequent::iterator loop_pos=(state_sequent::iterator)the_stack.back().subsequence_position;
the_stack.back().subsequence->insert(++loop_pos,new_loop);
}
continue;
}
else {
// simply append the state
PulseBlasterCommand* c=create_command(*this_state);
if (c==NULL) throw pulse_exception("failed state creation");
push_back(c);
}
}
else {
fprintf(stderr,"droped something");
// drop it...
}
// find the next state...
while (!the_stack.empty()) {
stackentry& last_entry=the_stack.back();
++last_entry.subsequence_position;
if (last_entry.subsequence_position!=last_entry.subsequence->end()) {
this_tag=*(the_stack.back().subsequence_position);
break;
}
// finish loop...
if (last_entry.subsequence->repeat>1) {
++last_entry.command;
if (last_entry.command==--end()) {
// special case long delay
back()->instruction=SpinCorePulseBlaster::LONG_DELAY;
back()->loop_count=last_entry.subsequence->repeat;
}
else {
// loop with several states
(**(last_entry.command)).instruction=SpinCorePulseBlaster::LOOP;
(**(last_entry.command)).loop_count=last_entry.subsequence->repeat;
back()->instruction=SpinCorePulseBlaster::END_LOOP;
back()->jump=last_entry.command;
}
}
// jump to next level
the_stack.pop_back();
}
if (the_stack.empty()) break;
}
delete flat;
// is that ok in all cases?
delete *dummy_pos;
erase(dummy_pos);
}

View File

@ -0,0 +1,88 @@
#include <list>
#include "core/states.h"
#include "core/stopwatch.h"
#include "drivers/pulsegen.h"
class PulseBlasterCommand;
/**
\brief holds the complete and detailed initialisation of the device
*/
class PulseBlasterProgram: public std::list<PulseBlasterCommand*> {
public:
double internal_clock_freq;
size_t minimum_interval;
/**
supports recursive traverse of states, in order insert workarounds and assemble PulseBlasterProgram
*/
struct stackentry {
state_sequent* subsequence;
state_sequent::iterator subsequence_position;
PulseBlasterProgram::iterator command;
};
PulseBlasterProgram(size_t mi=9, double cf=1e8) {
internal_clock_freq=cf;
minimum_interval=mi;
}
/// do not copy the list. This must be done by the derived class...
PulseBlasterProgram(const PulseBlasterProgram& orig);
/// insert some commands and get references right
int insert_ref(PulseBlasterProgram::iterator target_pos,
PulseBlasterProgram::const_iterator start_copy,
PulseBlasterProgram::const_iterator end_copy);
/// appends a sequence of states to existing program
void append_sequence(const state& seq);
/// write all configuration to a file to xml
virtual int write_to_file(FILE* out, size_t indent=0) const=0;
/// create a suitable state for this kind of program
virtual PulseBlasterCommand* create_command(const state& the_state)=0;
/// create a copy or a minimal empty state
virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* orig=NULL)=0;
virtual ~PulseBlasterProgram();
};
/**
\brief parameters for each pulseblaster command
*/
class PulseBlasterCommand {
public:
int ttls;
/// which instruction is used...
SpinCorePulseBlaster::opcode instruction;
/// data of instruction for loop counts
int loop_count;
/// length in units of clock cycle
size_t length;
/// the pulse program start iterator
const PulseBlasterProgram* program;
/// data of instruction for jumps
PulseBlasterProgram::const_iterator jump;
PulseBlasterCommand();
/// one for minimal length
PulseBlasterCommand(PulseBlasterProgram& p);
// a useful constructor for a simple CONTINUE state
PulseBlasterCommand(PulseBlasterProgram& p, int _ttls, double _length);
/// copy constructor
PulseBlasterCommand(const PulseBlasterCommand& orig);
/// write the command to a give file as xml tag
virtual int write_to_file(FILE* out, size_t indent=0) const=0;
virtual ~PulseBlasterCommand() {}
};

View File

@ -0,0 +1,265 @@
#include "SpinCore-PulseBlaster.h"
#include "PulseBlasterProgram.h"
#include "core/core.h"
#ifndef SP_DEBUG
# define SP_DEBUG 0
#endif
#ifdef __linux__
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() {
device_file_descriptor=open("/dev/" PULSEBLASTER_DEVICE_FILE_NAME,O_NONBLOCK|O_RDWR);
if (device_file_descriptor<0)
throw SpinCorePulseBlaster_error("could not open the device /dev/" PULSEBLASTER_DEVICE_FILE_NAME "\n");
}
SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() {
close(device_file_descriptor);
}
int SpinCorePulseBlasterLowlevel::write_data(const unsigned char* data, size_t size) {
size_t orig_size=size;
const unsigned int max_chunk_size=100*1<<10; // 100k commands
#if SP_DEBUG
stopwatch write_data_time;
write_data_time.start();
#endif
while (size>0) {
int result=write(device_file_descriptor, data, ((size>max_chunk_size)?max_chunk_size:size) );
// error handling
if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_data: error \"")+strerror(errno)+"\"");
if (result<0) {
char errorno[256];
snprintf(errorno, 256, "%d",result);
throw SpinCorePulseBlaster_error(std::string("write_register: ioctl returned negative value = ")+errorno);
}
// if (result==0) do some retry magic....
// success!
data+=result;
size-=result;
}
#if SP_DEBUG
fprintf(stderr, "wrote %d bytes in %f s\n", orig_size, write_data_time.elapsed());
#endif
return orig_size;
}
#endif
#ifdef __CYGWIN__
SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() {
const char spincore_dll[]="spinapi";
const char sp_outp_func_name[]="pb_outp";
const char sp_inp_func_name[]="pb_inp";
const char sp_Close_func_name[]="pb_close";
const char sp_Init_func_name[]="pb_init";
PBP_DLL = LoadLibrary(spincore_dll);
if (PBP_DLL==NULL) {
throw SpinCorePulseBlaster_error(std::string("could not open ")+spincore_dll+" library\n");
}
sp_outp = (__attribute__((stdcall))int(*)(unsigned short, int))GetProcAddress(PBP_DLL,sp_outp_func_name);
if (sp_outp==NULL) {
FreeLibrary(PBP_DLL);
PBP_DLL=NULL;
throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_outp");
}
sp_inp = (__attribute__((stdcall))int(*)(unsigned short))GetProcAddress(PBP_DLL, sp_inp_func_name);
if (sp_inp==NULL) {
FreeLibrary(PBP_DLL);
PBP_DLL=NULL;
throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_inp");
}
sp_Init = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL,sp_Init_func_name);
if (sp_Init==NULL) {
FreeLibrary(PBP_DLL);
PBP_DLL=NULL;
throw SpinCorePulseBlaster_error("could not access Pulseblaster Init function");
}
sp_Close = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL, sp_Close_func_name);
if (sp_Close==NULL) {
FreeLibrary(PBP_DLL);
PBP_DLL=NULL;
throw SpinCorePulseBlaster_error("could not access Pulseblaster Close function");
}
#if SP_DEBUG
__attribute__((stdcall))void (*sp_set_debug)(int flag);
sp_set_debug=(__attribute__((stdcall))void(*)(int))GetProcAddress(PBP_DLL,"pb_set_debug");
if (sp_set_debug!=NULL) sp_set_debug(1);
else fprintf(stderr, "could not load debug function from %s DLL\n",spincore_dll);
#endif
int result=sp_Init();
if (result!=0) {
fprintf(stderr, "sp_Init returned %d\n", result);
FreeLibrary(PBP_DLL);
PBP_DLL=NULL;
throw SpinCorePulseBlaster_error("could not initialise Pulseblaster card");
}
}
SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() {
if (sp_Close!=NULL) sp_Close();
sp_inp=NULL;
sp_outp=NULL;
sp_Close=NULL;
sp_Init=NULL;
if (PBP_DLL!=NULL) FreeLibrary(PBP_DLL);
}
#endif
void SpinCorePulseBlaster::reset_flags(unsigned int flags) {
unsigned char data[40];
data[0]=(flags&0xff000000)>>24;
data[1]=(flags&0xff0000)>>16;
data[2]=(flags&0xff00)>>8;
data[3]=flags&0xff;
write_register(0,0); // dev reset
write_register(2,4); // bytes per word
write_register(3,0xFF); // dev to program
write_register(4,0); //reset address counter
write_data(data,4);
write_register(5,0); //strobe clock
write_register(5,0); //strobe clock
}
void SpinCorePulseBlaster::set_program(const std::string& data) {
if (command_length==0)
throw SpinCorePulseBlaster_error("command length not set");
if (data.size()%command_length!=0)
throw SpinCorePulseBlaster_error("program data length does not match command length");
if (data.size()/command_length>max_commands) {
throw SpinCorePulseBlaster_error("program length exceeds maximum command number");
}
write_register(0,0); // dev reset
write_register(2,command_length); // bytes per word
write_register(3,0); // dev to program
write_register(4,0); //reset address counter
write_data(data);
}
void SpinCorePulseBlaster::run_pulse_program_w_sync(state& exp, double sync_freq) {
// set duration
state_sequent* seq=dynamic_cast<state_sequent*>(&exp);
if (seq==NULL)
throw pulse_exception("pulse program should be a sequence");
state_iterator i(*seq);
while (!i.is_last()) i.next_state();
duration=i.get_time();
#if SP_DEBUG
fprintf(stderr, "caluclated time of pulse program is %g\n",duration);
#endif
PulseBlasterProgram* prog=create_program(exp);
if (prog==NULL)
throw pulse_exception("could not create PulseBlasterProgram");
if (sync_freq>0 && sync_mask!=0) {
// synchronization with help of board P136
// P136 derives a single trigger slope from sampling clock of Spectrum MI4021
// using push_front(): we have to add the two commands in reverse order
PulseBlasterCommand* c;
// second command: clear sync mask
c=prog->create_command();
c->ttls=0x0;
c->instruction=SpinCorePulseBlaster::CONTINUE;
c->length=shortest_pulse;
prog->push_front(c);
// first command: wait for monoflop on P136 up again
c=prog->create_command();
c->ttls=sync_mask;
c->instruction=SpinCorePulseBlaster::WAIT;
c->length=shortest_pulse;
prog->push_front(c);
if (0) {
// zeroth command: set sync mask in advance, sometimes necessary
c=prog->create_command();
c->ttls=sync_mask;
c->instruction=SpinCorePulseBlaster::CONTINUE;
c->length=shortest_pulse+2;
prog->push_front(c);
}
duration+=2.0*shortest_pulse/clock+1.0/sync_freq;
}
// workaround for another PulseBlaster Bug:
// extra CONTINUE opcodes with little more duration to force proper initialization of all internal counters
while ((prog->size()>1 && (*(++(prog->begin())))->instruction!=CONTINUE) ||
(prog->size()>0 && (*(prog->begin()))->instruction!=CONTINUE)) {
PulseBlasterCommand* c;
c=prog->create_command();
c->instruction=SpinCorePulseBlaster::CONTINUE;
c->length=shortest_pulse+2;
prog->push_front(c);
duration+=(1.0+shortest_pulse)/clock;
}
// end: clear flags and stop pulseblaster
prog->push_back(prog->create_command());
prog->push_back(prog->create_command());
prog->back()->instruction=STOP;
#if SP_DEBUG
prog->write_to_file(stderr);
#endif
run_pulse_program(*prog);
time_running.start();
duration+=3.0*shortest_pulse/clock;
delete prog;
}
void SpinCorePulseBlaster::wait_till_end() {
double waittime=duration-time_running.elapsed();
double timeout=(waittime>10)?(waittime*0.01):0.1;
#if SP_DEBUG
fprintf(stderr,"waiting while pulseprogram running (%f)...",waittime);
#endif
// Bit zero is stopped; bit one is reset; bit two is running; bit three is waiting.
int status=get_status();
#if SP_DEBUG
fprintf(stderr,"status=0x%04x ",status);
#endif
// with synchronization, also waiting status can occur
while (waittime>-timeout && core::term_signal==0 && (status&(RUNNING|WAITING))!=0) {
if (waittime<1e-2)
waittime=1e-2;
else
waittime*=0.9;
#if SP_DEBUG
fprintf(stderr,"sleeping for %g seconds...",waittime);
fflush(stderr);
#endif
timespec nanosleep_time;
nanosleep_time.tv_sec=(time_t)floor(waittime);
nanosleep_time.tv_nsec=(long)ceil((waittime-nanosleep_time.tv_sec)*1e9);
nanosleep(&nanosleep_time,NULL);
waittime=duration-time_running.elapsed();
status=get_status();
#if SP_DEBUG
fprintf(stderr,"status: 0x%04x\n",status);
fflush(stderr);
#endif
}
if (core::term_signal!=0) {
//reset pulseblaster
stop();
reset_flags(0);
}
if (waittime<=-timeout) {
fprintf(stderr, "Pulseblaster: status=0x%04x, ran into timeout after %f s\nPulseblaster: aborting...", status, time_running.elapsed());
stop();
reset_flags(0);
status=get_status();
fprintf(stderr,"now: status=0x%04x\n", status);
}
#if SP_DEBUG
fprintf(stderr,"done\n");
#endif
}

View File

@ -0,0 +1,273 @@
#ifndef SPINCORE_PULSEBLASTER_H
#define SPINCORE_PULSEBLASTER_H
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <cerrno>
#include <unistd.h>
#include <core/stopwatch.h>
#include <drivers/pulsegen.h>
#ifndef SP_DEBUG
# define SP_DEBUG 1
#endif
class SpinCorePulseBlaster_error: public pulse_exception
{
public:
explicit SpinCorePulseBlaster_error(const std::string& msg) throw (): pulse_exception(msg) {}
explicit SpinCorePulseBlaster_error(const char* msg) throw (): pulse_exception(msg) {}
virtual ~SpinCorePulseBlaster_error() throw () {}
protected:
virtual const std::string prefix() const { return "ERROR (SpinCorePulseBlaster_error): "; }
};
#if !( defined(__linux__)||defined(__CYGWIN__))
# error "Sorry, we are not prepared for this target!"
#endif
#ifdef __linux__
# include <fcntl.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <unistd.h>
# include "pulseblaster.h"
#endif
#ifdef __CYGWIN__
# include <windows.h>
#endif
class SpinCorePulseBlasterLowlevel {
protected:
# ifdef __linux__
/**
device file handle
*/
int device_file_descriptor;
# endif
# ifdef __CYGWIN__
/**
the loaded dll
*/
HINSTANCE PBP_DLL;
/**
the pulseblaster communication function sp_outp
\param address address register in board's internal memory
\param value value to store
*/
__attribute__((stdcall)) int (*sp_outp)(unsigned short address, int value);
/**
the pulseblaster communication function sp_inp
\param address addres register in board's internal memory
*/
__attribute__((stdcall)) int (*sp_inp)(unsigned short address);
/**
initialisation of the Pulseblaster board
*/
__attribute__((stdcall)) int (*sp_Init)();
/**
close access to Pulseblaster board
*/
__attribute__((stdcall)) int (*sp_Close)();
#endif
SpinCorePulseBlasterLowlevel();
#ifdef __linux__
inline int write_register(int reg, int data) {
int result=ioctl(device_file_descriptor,IOCTL_OUTB,(reg&0xff)<<8|(data&0xff));
if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_register: ioctl error \"")+strerror(errno)+"\"");
if (result!=0) {
char errorno[256];
snprintf(errorno, 256, "%d",result);
throw SpinCorePulseBlaster_error(std::string("write_register: ioctl returned nonzero value = ")+errorno);
}
return result;
}
/**
writes data to pulseblaster device
care about buffer length and write in chunks to avoid freeze of user space
*/
int write_data(const unsigned char* data, size_t size);
inline int read_register(int reg) {
int result=ioctl(device_file_descriptor,IOCTL_INB, reg&0xff);
if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_register: ioctl error \"")+strerror(errno)+"\"");
if (result<0) throw SpinCorePulseBlaster_error("read_register: ioctl returned nonzero value");
return result;
}
#endif
#ifdef __CYGWIN__
inline int write_register(unsigned int reg, unsigned int data) {
int result=sp_outp(reg,data);
#if SP_DEBUG
fprintf(stderr, "sp_outp(0x%02x,0x%02x)\n",reg,data);
#endif
if (result!=0) throw SpinCorePulseBlaster_error("write_register returned nonzero value");
return result;
}
inline int write_data(const unsigned char* data, size_t size) {
int result=0;
size_t i;
for (i=0; i<size && result==0; ++i) {
result=write_register(6,data[i]);
}
if (result!=0) throw SpinCorePulseBlaster_error("write_data: error while writing");
return i;
}
inline int read_register(int reg) {
int result=sp_inp(reg);
if (result<0) throw SpinCorePulseBlaster_error("read_register: error while reading");
return result;
}
#endif
inline int write_data(const std::string& data) {
return write_data((const unsigned char*)data.c_str(),data.size());
}
~SpinCorePulseBlasterLowlevel();
};
class PulseBlasterProgram;
class SpinCorePulseBlaster: protected SpinCorePulseBlasterLowlevel, public pulsegen {
public:
/**
clock in Hz
*/
double clock;
/**
shortest pulse in clock cycles
*/
unsigned int shortest_pulse;
/**
maximum number of commands
*/
unsigned int max_commands;
/**
command/flags wordlength
*/
unsigned int command_length;
/**
is started with pulse program
*/
stopwatch time_running;
/**
expected duration in seconds, nonzero if pulseprogram is running
*/
double duration;
/**
id of ttlout sections affecting this device
*/
int ttl_device_id;
/**
bit mask to set while WAIT command for synchronization is executed
no synchronization when zero.
*/
unsigned int sync_mask;
public:
enum opcode {CONTINUE=0, STOP=1, LOOP=2, END_LOOP=3, JSR=4, RTS=5, BRANCH=6, LONG_DELAY=7, WAIT=8};
/**
status codes retured by get_status function
they are combinable like bit masks
*/
enum statuscode { STOPPED=1<<0, /// Bit 0 - Stopped
RESET=1<<1, /// Bit 1 - Reset
RUNNING=1<<2, /// Bit 2 - Running
WAITING=1<<3, /// Bit 3 - Waiting
SCANNING=1<<4 /// Bit 4 - Scanning (RadioProcessor boards only)
};
SpinCorePulseBlaster(int c_length, double the_clock) {
clock=the_clock;
shortest_pulse=9;
command_length=c_length;
max_commands=1<<15;
}
void reset_flags(unsigned int flags=0);
inline void set_initialized() {
write_register(7,0);
}
/**
starts execution of pulseprogram, needs an initialized pulseblaster
*/
inline void start() {
write_register(1,0);
}
/**
stops execution of pulseprogram
*/
inline void stop() {
write_register(0,0);
}
/**
returns status information of pulseblaster board
\return status 1: stopped, 2: reset, 4: running, 8: waiting
*/
inline int get_status() {
return read_register(0);
}
void set_program(const std::string& data);
/**
run a experiment given as state sequence
*/
virtual void run_pulse_program(state& exp) {
run_pulse_program_w_sync(exp, 0);
}
/**
run a experiment given as state sequence, but optionally synchronize sequence start with ADC sampling clock
*/
virtual void run_pulse_program_w_sync(state& exp, double sync_freq=0);
/**
needed by run_pulse_program method
*/
virtual PulseBlasterProgram* create_program(state& exp)=0;
/**
needed by run_pulse_program method
*/
virtual void run_pulse_program(const PulseBlasterProgram& p)=0;
/**
wait till end of pulseprogram
*/
virtual void wait_till_end();
};
#endif /* SPINCORE_PULSEBLASTER_H */

View File

@ -0,0 +1,185 @@
#include <SpinCore-PulseBlaster.h>
#include <unistd.h>
class PB_test: SpinCorePulseBlasterLowlevel {
public:
PB_test(): SpinCorePulseBlasterLowlevel() {}
int testprog(int n=1) {
write_register(0x00,0x00);
write_register(0x02,0x0a);
write_register(0x03,0x00);
write_register(0x04,0x00);
// do nothing in first place (no loops in first place!!!!)
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// pulse 0.6 us
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x07);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x39);
// wait 7.7 us
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,n&0xff);
write_register(0x06,0xff); // change 0xff to 0xfe
// trigger
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x07);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x50);
write_register(0x06,0x0c);
// clear flags
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// stop
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x01);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// end programming
write_register(0x07,0x00);
// run
write_register(0x01,0x00);
sleep(1);
return 0;
}
int testinterval(unsigned long int tics) {
write_register(0x00,0x00);
write_register(0x02,0x0a);
write_register(0x03,0x00);
write_register(0x04,0x00);
// do nothing in first place (no loops in first place!!!!)
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// pulse 0.1 us
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x07);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0xfe);
// wait tics time units
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
// delay (32 bit)
write_register(0x06,(tics>>24)&0xff);
write_register(0x06,(tics>>16)&0xff);
write_register(0x06,(tics>>8)&0xff);
write_register(0x06,tics&0xff);
// trigger
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x07);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0xfe);
// clear flags
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// stop
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x01);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x00);
write_register(0x06,0x06);
// end programming
write_register(0x07,0x00);
// run
write_register(0x01,0x00);
sleep(1);
return 0;
}
~PB_test(){}
};
int main(int argc, char* argv[]) {
int n=9;
if (argc>1 && argv[1]!=0) {
n=strtoul(argv[1],NULL,0);
}
printf("%d\n",n);
return PB_test().testinterval(n);
}

View File

@ -0,0 +1,945 @@
/*
* pulseblaster.c - Communication with pulseblaster
* author: Achim Gaedke <achim.gaedke@physik.tu-darmstadt.de>
* created: February 2005
* the amcc_outb routine comes from SpinCore
* initially the module is built from example 4 of "The Linux Kernel Module Programming Guide"
* the version of June 2008 is written with the help of "Linux device drivers"
* Jonathan Corbet, Alessandro Rubini, and Greg Kroah-Hartman, O' Reilly, 3rd Edition
*/
#include <linux/kernel.h> /* We're doing kernel work */
#include <linux/module.h> /* Specifically, a module */
#include <linux/fs.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/version.h>
#include <asm/uaccess.h> /* for get_user and put_user */
#include <asm/io.h>
#include "pulseblaster.h"
#define SUCCESS 0
#define DEVICE_NAME "pulseblaster"
enum pulseblaster_board {
PBB_DEBUG = 0,
PBB_GENERIC_AMCC,
PBB_GENERIC_PCI,
};
/* number of found and allocated devices */
static int pb_dev_no=0;
/*
* dynamic allocated device number
*/
/* start of allocated minor number(s)*/
static dev_t pb_dev_no_start;
struct pulseblaster_device {
/*
* Is the device open right now? Used to prevent
* concurent access into the same device
*/
int device_open;
/**
* The type of this board
*/
enum pulseblaster_board boardtype;
/**
* The pci device for this pulseblaster
*/
struct pci_dev *pciboard;
/*
* the base io address
* 0 means debug mode without hardware accesss, writes everything to kernel log
*/
unsigned long base_addr;
/*
* protect access to io memory
*/
spinlock_t io_lock;
/*
* char dev associated with it
*/
struct cdev cdev;
};
/* array of char_devices */
/* todo: make this a pointer array with dynamic allocation... */
#define pulseblaster_max_devno 4
static struct pulseblaster_device pb_devs[pulseblaster_max_devno];
// a lock for that structure
static spinlock_t pb_devs_lock;
// debug version
static struct pulseblaster_device pb_dev_debug;
static int base_address=-1;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Achim Gaedke");
MODULE_DESCRIPTION("Driver for SpinCore's PulseBlaster");
module_param(base_address, int, S_IRUGO);
MODULE_PARM_DESC(base_address, "not supported anymore, IO base addresses of device are detected automatically");
/*************************************************************************************/
/* code taken form spincore example:
* This function writes a byte to a board that uses the amcc chip.
* SP3 and previous revision boards use this interface. Later designs
* can be programmed directly.
*/
#if 1
// version without delay (faster)
#define pb_out(bwl) out##bwl
#define pb_in(bwl) in##bwl
#else
// version with extra delay (works on odd hardware, too)
#define pb_out(bwl) out##bwl##_p
#define pb_in(bwl) in##bwl##_p
#endif
static int pb_outb_debug(char data, unsigned short address, unsigned long my_base_address)
{
printk("%s: reg %02x=%02x\n",DEVICE_NAME, 0xff&address, 0xff&data);
return 0;
}
static int amcc_outb(char data, unsigned short address, unsigned long my_base_address) {
unsigned int MAX_RECV_TIMEOUT = 10;
unsigned int RECV_START, RECV_TOGGLE, RECV_TIMEOUT = 0;
int XFER_ERROR = 0;
int RECV_POLLING = 0;
unsigned int OGMB = 0x0C;
unsigned int CHK_RECV = 0x1F;
unsigned int SIG_TRNS = 0x0F;
unsigned int CLEAR31 = 0x00000001;
unsigned int CLEAR24 = 0x000000FF;
unsigned int CLEAR28 = 0x0000000F;
unsigned int SET_XFER = 0x01000000;
unsigned int Temp_Address = address;
unsigned int Temp_Data = data;
if (my_base_address==0) {
return pb_outb_debug(data, address, my_base_address);
}
// Prepare Address Transfer
Temp_Address &= CLEAR28;
Temp_Address |= SET_XFER;
// Prepare Data Transfer
Temp_Data &= CLEAR24;
Temp_Data |= SET_XFER;
// Clear the XFER bit (Should already be cleared)
pb_out(b) (0,my_base_address+SIG_TRNS);
// Transfer Address
// Read RECV bit from the Board
RECV_START = pb_in(b) (my_base_address+CHK_RECV);
RECV_START &= CLEAR31; // Only Save LSB
// Send Address to OGMB
pb_out(l) (Temp_Address, my_base_address+OGMB);
RECV_POLLING = 1; // Set Polling Flag
RECV_TIMEOUT = 0;
while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT))
{
RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV);
RECV_TOGGLE &= CLEAR31; // Only Save LSB
if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different
else RECV_TIMEOUT++;
if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2;
}
// Transfer Complete (Clear) Signal
pb_out(b)(0, my_base_address+SIG_TRNS);
// Transfer Data
// Read RECV bit from the Board
RECV_START = pb_in(b)(my_base_address+CHK_RECV);
RECV_START &= CLEAR31; // Only Save LSB
// Send Data to OGMB
pb_out(l)(Temp_Data, my_base_address+OGMB);
RECV_POLLING = 1; // Set Polling Flag
RECV_TIMEOUT = 0;
while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT))
{
// Check for Toggled RECV
RECV_TOGGLE = pb_in(b)(my_base_address+CHK_RECV);
RECV_TOGGLE &= CLEAR31; // Only Save LSB
if (RECV_TOGGLE != RECV_START) RECV_POLLING = 0; // Finished if Different
else RECV_TIMEOUT++;
if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2;
}
// Transfer Complete (Clear) Signal
pb_out(b)(0, my_base_address+SIG_TRNS);
return XFER_ERROR;
}
static int pb_outb_pci(char data, unsigned short address, unsigned long my_base_address)
{
if (my_base_address == 0)
return pb_outb_debug(data, address, my_base_address);
pb_out(b)(data, my_base_address + address);
return 0;
}
/**
* write to byte register on board
*/
static int pb_dev_outb(struct pulseblaster_device *my_dev, char data, unsigned short address)
{
unsigned long my_base_address;
if (my_dev == NULL)
return -1;
my_base_address = my_dev->base_addr;
switch (my_base_address != 0
? my_dev->boardtype
: PBB_DEBUG)
{
case PBB_DEBUG:
return pb_outb_debug(data, address, my_base_address);
case PBB_GENERIC_AMCC:
return amcc_outb(data, address, my_base_address);
case PBB_GENERIC_PCI:
return pb_outb_pci(data, address, my_base_address);
default:
return -1;
}
return -1;
}
static int pb_inb_debug(unsigned int address, unsigned long my_base_address)
{
printk("%s: reg(%02x)=0 (guessed)\n",DEVICE_NAME, 0xff&address);
return 0;
}
/**
* Read a byte from a board using the AMCC chip
*/
static int amcc_inb(unsigned int address, unsigned long my_base_address) {
unsigned int MAX_RECV_TIMEOUT = 10000;
unsigned int RECV_START, RECV_STOP, RECV_TOGGLE, RECV_TIMEOUT = 0;
int XFER_ERROR = 0;
int RECV_POLLING = 0;
unsigned int CHK_RECV = 0x1F;
unsigned int SIG_TRNS = 0x0F;
unsigned int ICMB = 0x1C;
unsigned int CLEAR24 = 0x000000FF;
unsigned int BIT1 = 0x00000002;
unsigned int INV1 = 0x000000FD;
unsigned short READ_ADDR = 0x9;
int Toggle = 0;
int Toggle_Temp = 0;
unsigned int Temp_Data = 0;
if (my_base_address==0) {
return pb_inb_debug(address, my_base_address);
}
// CHEK FOR 1 in MD1
if ((XFER_ERROR=amcc_outb(address, 8, my_base_address))!=0 ||
(XFER_ERROR=amcc_outb(0, READ_ADDR, my_base_address))!=0) // Tell board to start a read cycle
return XFER_ERROR;
RECV_POLLING = 1; // Set Polling Flag
RECV_TIMEOUT = 0;
RECV_START = 0x2; // Value expected when data is ready
while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) {
// Check for Toggled RECV
//RECV_TOGGLE = _inp(CHK_RECV);
RECV_TOGGLE = pb_in(b) (my_base_address+CHK_RECV);
RECV_TOGGLE &= BIT1; // Only Save Bit 1
if (RECV_TOGGLE == RECV_START) RECV_POLLING = 0; // Finished if Different
else RECV_TIMEOUT++;
if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -2;
}
if (XFER_ERROR != 0) {
return XFER_ERROR;
}
//Temp_Data = _inp(ICMB);
// Read RECV bit from the Board
Temp_Data = pb_in(b)(my_base_address+ICMB);
Temp_Data &= CLEAR24;
//Toggle = _inp(SIG_TRNS);
Toggle = pb_in(b) (my_base_address+SIG_TRNS);
Toggle_Temp = Toggle & BIT1; // Only Save Bit 1
if (Toggle_Temp == 0x0)
{
Toggle |= BIT1; // If Bit 1 is zero, set it to 1
}
else
{
Toggle &= INV1; // IF Bit 1 is 1, set it to 0
}
//_outp(SIG_TRNS, Toggle);
pb_out(b) (Toggle,my_base_address+SIG_TRNS);
RECV_POLLING = 1; // Set Polling Flag
RECV_TIMEOUT = 0;
RECV_STOP = 0x0;
while ((RECV_POLLING == 1) && (RECV_TIMEOUT < MAX_RECV_TIMEOUT)) {
// Check for Toggled RECV
//RECV_TOGGLE = _inp(CHK_RECV);
RECV_TOGGLE = pb_in(b) (my_base_address + CHK_RECV);
RECV_TOGGLE &= BIT1; // Only Save Bit 1
if (RECV_TOGGLE == RECV_STOP) RECV_POLLING = 0; // Finished if Different
else RECV_TIMEOUT++;
if (RECV_TIMEOUT == MAX_RECV_TIMEOUT) XFER_ERROR = -3;
}
if (XFER_ERROR != 0) {
return XFER_ERROR;
}
return Temp_Data;
}
static int pb_inb_pci(unsigned int address, unsigned long my_base_address)
{
if (my_base_address == 0)
return pb_inb_debug(address, my_base_address);
return pb_in(b)(my_base_address + address);
}
/**
* Read byte register from board
*/
static int pb_dev_inb(struct pulseblaster_device *my_dev, unsigned short address)
{
unsigned long my_base_address;
if (my_dev == NULL)
return -1;
my_base_address = my_dev->base_addr;
switch (my_base_address != 0
? my_dev->boardtype
: PBB_DEBUG)
{
case PBB_DEBUG:
return pb_inb_debug(address, my_base_address);
case PBB_GENERIC_AMCC:
return amcc_inb(address, my_base_address);
case PBB_GENERIC_PCI:
return pb_inb_pci(address, my_base_address);
default:
return -1;
}
return -1;
}
/**
* Setup and get the base_addr
*/
static unsigned long pb_dev_setup_baseaddr(struct pulseblaster_device *my_dev)
{
unsigned long addr = 0x0;
if (my_dev == NULL)
return 0x0;
if ((my_dev->boardtype != PBB_DEBUG)
&& (my_dev->pciboard != NULL))
{
addr = pci_resource_start(my_dev->pciboard, 0);
}
my_dev->base_addr = addr;
return addr;
}
static inline bool pb_is_debug(struct pulseblaster_device *my_dev)
{
return ((my_dev->boardtype == PBB_DEBUG)
|| (my_dev->pciboard == NULL));
}
/*
* This is called whenever a process attempts to open the device file
*/
static int device_open(struct inode *inode, struct file *file)
{
struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev);
file->private_data=my_dev;
if (pb_is_debug(my_dev))
printk("%s: device_open(%p)\n", DEVICE_NAME,file);
/*
* We don't want to talk to two processes at the same time
*/
if (my_dev->device_open)
return -EBUSY;
my_dev->device_open++;
try_module_get(THIS_MODULE);
return SUCCESS;
}
/*
* Device released (a.k.a. closed)
*/
static int device_release(struct inode *inode, struct file *file)
{
struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev);
pb_dev_setup_baseaddr(my_dev);
if (pb_is_debug(my_dev))
printk("%s: device_release(%p,%p)\n",DEVICE_NAME, inode, file);
/*
* reset device
*/
if (0) {
// this procedure works only on DDS cards
// DDS Manual
spin_lock(&(my_dev->io_lock));
pb_dev_outb(my_dev, 0, 0); //dev reset
pb_dev_outb(my_dev, 4, 2); //bytes per word
pb_dev_outb(my_dev, 0xFF, 3); //dev to program
pb_dev_outb(my_dev, 0, 4); //reset address counter
pb_dev_outb(my_dev, 0, 6); //data out
pb_dev_outb(my_dev, 0, 6); //data out
pb_dev_outb(my_dev, 0, 6); //data out
pb_dev_outb(my_dev, 0, 6); //data out
pb_dev_outb(my_dev, 0, 5); //strobe clock
pb_dev_outb(my_dev, 0, 5); //strobe clock
spin_unlock(&(my_dev->io_lock));
}
if (0) {
spin_lock(&(my_dev->io_lock));
// this procedure is successful for 24Bit cards
// SpinCore by mail Oct 10th 2007
pb_dev_outb(my_dev, 0,6);//store 0's to memory
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,6);
pb_dev_outb(my_dev, 0,0);//dev reset
pb_dev_outb(my_dev, 4,2);//bytes per word
pb_dev_outb(my_dev, 0,3);//write to internal memory
pb_dev_outb(my_dev, 0,4);//clear address counter
pb_dev_outb(my_dev, 0,6);//data out
pb_dev_outb(my_dev, 0,6);//data out
pb_dev_outb(my_dev, 0,6);//data out
pb_dev_outb(my_dev, 0,6);//data out
pb_dev_outb(my_dev, 7,7);//programming finished
pb_dev_outb(my_dev, 7,1);//trigger
spin_unlock(&(my_dev->io_lock));
}
/*
* We're now ready for our next caller
*/
my_dev->device_open--;
module_put(THIS_MODULE);
return SUCCESS;
}
/*
* This function is called whenever a process which has already opened the
* device file attempts to read from it.
*/
static ssize_t device_read(struct file *file, /* see include/linux/fs.h */
char __user * buffer, /* buffer to be
* filled with data */
size_t length, /* length of the buffer */
loff_t * offset)
{
// not implemented
return 0;
}
/*
* This function is called when somebody tries to
* write into our device file.
* everything goes to register 6: data
*/
static ssize_t
device_write(struct file *file,
const char __user * buffer, size_t length, loff_t * offset)
{
struct pulseblaster_device* my_dev=(struct pulseblaster_device*)file->private_data;
register int i=0;
pb_dev_setup_baseaddr(my_dev);
while (1) {
// sometimes
unsigned char temp_byte;
int retval;
get_user(temp_byte, buffer + i);
// the hardware access
spin_lock(&(my_dev->io_lock));
retval = pb_dev_outb(my_dev, temp_byte,6);
spin_unlock(&(my_dev->io_lock));
if (retval!=0) {
printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, retval);
return -EIO; // make an ordinary io error
}
i++;
/* break codition */
if (i>=length) break;
/* be decent to all other processes --- at least sometimes */
if ((i & ((1<<14)-1)) == 0) schedule();
}
/*
* Again, return the number of input characters used
*/
return i;
}
/*
* This function is called whenever a process tries to do an ioctl on our
* device file. We get two extra parameters (additional to the inode and file
* structures, which all device functions get): the number of the ioctl called
* and the parameter given to the ioctl function.
*
* If the ioctl is write or read/write (meaning output is returned to the
* calling process), the ioctl call returns the output of this function.
*
*/
static long device_ioctl(
struct file *file, /* ditto */
unsigned int ioctl_num, /* number and param for ioctl */
unsigned long ioctl_param)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct pulseblaster_device* my_dev=container_of(inode->i_cdev, struct pulseblaster_device, cdev);
int ret_val=SUCCESS;
pb_dev_setup_baseaddr(my_dev);
if (ioctl_num==IOCTL_OUTB){
unsigned char reg=(ioctl_param>>8)&0xFF;
unsigned char val=ioctl_param&0xFF;
/*
check for register boundaries!
*/
if (reg>7) {
printk("%s: got bad register number %02x\n",DEVICE_NAME,0x0ff&reg);
ret_val=-EINVAL;
}
else {
spin_lock(&(my_dev->io_lock));
ret_val = pb_dev_outb(my_dev, val, reg);
spin_unlock(&(my_dev->io_lock));
if (ret_val!=0) {
printk("%s: IO Error, pb_dev_outb returned %d\n",DEVICE_NAME, ret_val);
ret_val=-EIO; // make an ordinary io error
}
}
}
else if (ioctl_num==IOCTL_INB) {
unsigned char reg=ioctl_param&0xFF;
//printk("%s: reading register number %02x\n",DEVICE_NAME,0x0ff&reg);
spin_lock(&(my_dev->io_lock));
ret_val = pb_dev_inb(my_dev, reg);
spin_unlock(&(my_dev->io_lock));
//printk("%s: found %02x=%02x\n", DEVICE_NAME, 0x0ff&reg, 0x0ff&val);
if (ret_val<0) {
printk("%s: IO Error, pb_dev_inb returned %d\n",DEVICE_NAME, ret_val);
ret_val=-EIO; // make an ordinary io error
}
}
else {
printk("%s: unknown ioctl request number %d\n",DEVICE_NAME, ioctl_num);
ret_val=-EINVAL;
}
return ret_val;
}
/* Module Declarations */
/*
* This structure will hold the functions to be called
* when a process does something to the device we
* created. Since a pointer to this structure is kept in
* the devices table, it can't be local to
* init_module. NULL is for unimplemented functions.
*/
struct file_operations pulseblaster_fops = {
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.release = device_release, /* a.k.a. close */
};
/*
here all the basic pci stuff follows
*/
/*
* no hotpluging after initialisation...
*/
static int pulseblaster_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) {
int ret_val;
spin_lock(&pb_devs_lock);
// check wether device numbers were already allocated
if (pb_dev_no_start!=0) {
//we are too late
spin_unlock(&pb_devs_lock);
printk("%s: Sorry, hotpluging not supported!\n", DEVICE_NAME);
return -1;
}
if (pb_dev_no>=pulseblaster_max_devno) {
spin_unlock(&pb_devs_lock);
printk("%s: Sorry, maximum number of allocatable devices reached !\n", DEVICE_NAME);
return -1;
}
/* do i need this?, what about pci_enable_device_bars() */
ret_val=pci_enable_device(dev);
if (ret_val!=0) {
spin_unlock(&pb_devs_lock);
printk("%s: failed to enable pci device!\n",DEVICE_NAME);
return -1;
}
#ifndef CONFIG_XEN
// why is pci_request_region not defined in a XEN kernel?
/* exclusive use */
ret_val=pci_request_region(dev, 0, DEVICE_NAME);
if (ret_val!=0) {
spin_unlock(&pb_devs_lock);
printk("%s: failed to enable pci device!\n",DEVICE_NAME);
pci_disable_device(dev);
return -1;
}
#else
# warning "in XEN version pci_request_region and pci_release_region are not defined, omitting function call"
#endif
/* initialize the structure */
pb_devs[pb_dev_no].device_open=0;
spin_lock_init(&(pb_devs[pb_dev_no].io_lock));
pb_devs[pb_dev_no].pciboard=dev;
pb_devs[pb_dev_no].boardtype = id->driver_data;
pb_devs[pb_dev_no].base_addr = 0x0;
// todo: inform about slots as a hint for the physical location of the board!
printk("%s: found a pci board with i/o base address 0x%lx, assigning no %d\n",
DEVICE_NAME,
(unsigned long)pci_resource_start(dev, 0),
pb_dev_no);
++pb_dev_no;
spin_unlock(&pb_devs_lock);
return SUCCESS;
}
static void pulseblaster_pci_remove(struct pci_dev* dev) {
int i=0;
/* for hotplugging, maintain the list in a smart way
here: remove only dangling device pointer!
*/
spin_lock(&pb_devs_lock);
for (i=0; i<pb_dev_no; ++i) {
if (pb_devs[i].pciboard==dev) {
spin_lock(&(pb_devs[i].io_lock));
pb_devs[i].pciboard=NULL;
pb_devs[i].boardtype = PBB_DEBUG;
pb_devs[i].base_addr = 0x0;
spin_unlock(&(pb_devs[i].io_lock));
printk("%s: releasing device no %d\n", DEVICE_NAME, i);
break;
}
}
spin_unlock(&pb_devs_lock);
/* do the pci stuff only */
pci_disable_device(dev);
#ifndef CONFIG_XEN
// why is pci_release_region not defined in a XEN kernel?
pci_release_region(dev, 0);
#endif
}
static struct pci_device_id pulseblaster_pci_ids[] = {
{PCI_DEVICE(0x10e8, 0x8852), .driver_data = PBB_GENERIC_AMCC},
{PCI_DEVICE(0x10e8, 0x8878), .driver_data = PBB_GENERIC_PCI},
{0,},
};
MODULE_DEVICE_TABLE(pci, pulseblaster_pci_ids);
static struct pci_driver pulseblaster_pci_driver = {
.name = DEVICE_NAME,
.id_table=pulseblaster_pci_ids,
.probe=pulseblaster_pci_probe,
.remove=pulseblaster_pci_remove,
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
#define WITH_DRVDATA
#endif
static struct class *pulseblaster_class = NULL;
static void pb_class_setup(void)
{
/*
* create class and register devices in /sys
*/
pulseblaster_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(pulseblaster_class))
{
printk(KERN_ERR "%s: failed to register class", DEVICE_NAME);
pulseblaster_class = NULL;
}
}
static void pb_class_destroy(void)
{
if (pulseblaster_class)
class_destroy(pulseblaster_class);
pulseblaster_class = NULL;
}
/**
* Add device to our class
*/
static void pb_class_device_create(dev_t devno, int nr)
{
struct device *dev;
if (pulseblaster_class == NULL)
return;
if (nr == -1)
{
dev = device_create(pulseblaster_class, NULL, devno,
#ifdef WITH_DRVDATA
NULL,
#endif
DEVICE_NAME"_dbg");
}
else
{
dev = device_create(pulseblaster_class, NULL, devno,
#ifdef WITH_DRVDATA
NULL,
#endif
DEVICE_NAME"%d", nr);
}
if (IS_ERR(dev))
{
printk(KERN_WARNING "%s: device_create for %d failed\n",
DEVICE_NAME, nr);
}
}
/**
* Remove device from class
*/
static void pb_class_device_destroy(dev_t devno)
{
if (pulseblaster_class == NULL)
return;
device_destroy(pulseblaster_class, devno);
}
#else
static void pb_class_setup(void)
{
printk(KERN_WARNING "%s: No udev support\n", DEVICE_NAME);
}
static void pb_class_destroy(void)
{
}
static void pb_class_device_create(dev_t devno, int nr)
{
}
static void pb_class_device_destroy(dev_t devno)
{
}
# warning "No class support, hotplug/udev wont wrok"
#endif
/*
* Initialize the module - Register the character device
*/
static int __init init_pulseblaster_module(void)
{
int ret_val;
int i;
int major_dev_num, minor_dev_num;
if (base_address!=-1) {
printk("%s: no longer supporting 'base_address' parameter\n", DEVICE_NAME);
}
pb_dev_no_start=0;
pb_dev_no=0;
// lock for manipulating the device list
spin_lock_init(&pb_devs_lock);
// always provide a debug device
pb_dev_debug.device_open=0;
spin_lock_init(&(pb_dev_debug.io_lock));
pb_dev_debug.pciboard=0x0; // this is the debug flag for amcc functions
pb_dev_debug.boardtype = PBB_DEBUG;
pb_dev_debug.base_addr = 0x0;
// register code for pci bus scanning
ret_val=pci_register_driver(&pulseblaster_pci_driver);
// todo error checking
if (ret_val<0) {
printk("%s: registering the pci driver failed", DEVICE_NAME);
return ret_val;
}
// get device ids
ret_val= alloc_chrdev_region(&pb_dev_no_start, 0, pb_dev_no+1, DEVICE_NAME);
major_dev_num=MAJOR(pb_dev_no_start);
minor_dev_num=MINOR(pb_dev_no_start);
if (ret_val < 0) {
printk("%s failed with %d\n",
"Sorry, registering the character device failed\n", ret_val);
pci_unregister_driver(&pulseblaster_pci_driver);
return ret_val;
}
pb_class_setup();
// register debug device
{
dev_t devno = MKDEV(major_dev_num, minor_dev_num+pb_dev_no);
cdev_init(&(pb_dev_debug.cdev), &pulseblaster_fops);
pb_dev_debug.cdev.owner=THIS_MODULE;
pb_dev_debug.cdev.ops=&pulseblaster_fops;
ret_val=cdev_add(&(pb_dev_debug.cdev), devno, 1);
pb_class_device_create(devno, -1);
}
spin_lock(&pb_devs_lock);
for (i=0; i<pb_dev_no; ++i) {
dev_t devno = MKDEV(major_dev_num, minor_dev_num+i);
/* register char dev */
cdev_init(&(pb_devs[i].cdev), &pulseblaster_fops);
pb_devs[i].cdev.owner=THIS_MODULE;
pb_devs[i].cdev.ops=&pulseblaster_fops;
ret_val=cdev_add(&(pb_devs[i].cdev), devno, 1);
if (ret_val<0) {
printk("%s: failed to register char device", DEVICE_NAME);
// todo cleanup and return;
}
pb_class_device_create(devno, i);
}
spin_unlock(&pb_devs_lock);
return 0;
}
module_init(init_pulseblaster_module);
/*
* Cleanup - unregister the appropriate file from /proc
*/
static void __exit cleanup_pulseblaster_module(void)
{
/*
* Unregister the device
* ToDo: check use count?!
*/
int i;
int major_dev_num=MAJOR(pb_dev_no_start);
int minor_dev_num=MINOR(pb_dev_no_start);
spin_lock(&pb_devs_lock);
for (i=0; i<pb_dev_no; ++i)
{
pb_class_device_destroy(MKDEV(major_dev_num, minor_dev_num+i));
}
spin_unlock(&pb_devs_lock);
pb_class_device_destroy(MKDEV(major_dev_num, minor_dev_num+pb_dev_no));
pb_class_destroy();
spin_lock(&pb_devs_lock);
for (i=0; i<pb_dev_no; ++i) {
cdev_del(&(pb_devs[i].cdev));
}
spin_unlock(&pb_devs_lock);
cdev_del(&(pb_dev_debug.cdev));
unregister_chrdev_region(pb_dev_no_start, pb_dev_no+1);
pci_unregister_driver(&pulseblaster_pci_driver);
pb_dev_no=0;
}
module_exit(cleanup_pulseblaster_module);

View File

@ -0,0 +1,23 @@
/*
* pulseblaster.h - the header file with the ioctl definitions.
*
* The declarations here have to be in a header file, because
* they need to be known both to the kernel module
* and the process calling ioctl
*/
#ifndef PULSEBLASTER_H
#define PULSEBLASTER_H
#include <linux/ioctl.h>
#define PULSEBLASTER_DEVICE_MAGIC_NUMBER 'p'
#define IOCTL_OUTB _IOW(PULSEBLASTER_DEVICE_MAGIC_NUMBER, 1, unsigned long)
#define IOCTL_INB _IOR(PULSEBLASTER_DEVICE_MAGIC_NUMBER, 2, unsigned long)
/*
* The name of the device file
*/
#define PULSEBLASTER_DEVICE_FILE_NAME "pulseblaster"
#endif

View File

@ -0,0 +1,71 @@
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include "SpinCore-PulseBlaster.h"
class SpinCorePulseBlaster24Bit: public SpinCorePulseBlaster {
public:
SpinCorePulseBlaster24Bit (double the_clock=100e6): SpinCorePulseBlaster(10,the_clock) {
}
void append_command(std::string& data, int flags, opcode inst, int inst_data, double length) {
unsigned char command[command_length];
if (inst>8)
throw SpinCorePulseBlaster_error("instruction code not known");
double tmpdelay=(clock*length-3.0);
if (tmpdelay<(shortest_pulse-3))
throw SpinCorePulseBlaster_error("pulse is shorter than allowed");
if (tmpdelay>(pow(2,32)-1))
throw SpinCorePulseBlaster_error("pulse is longer than allowed");
unsigned int delay=(unsigned int)tmpdelay;
// Output, Control Word 1st Byte
command[0]=(flags&0xff0000)>>16;
// Output, Control Word 2nd Byte
command[1]=(flags&0xff00)>>8;
// Output, Control Word 3rd Byte
command[2]=flags&0xff;
// Data Field 1st Byte
command[3]=(inst_data&0x0ff000)>>12;
// Data Field 2nd Byte
command[4]=(inst_data&0xff0)>>4;
// Data Field 3rd Byte and opcode
command[5]=(inst_data&0xf)<<4|(inst&0xf);
// Delay Count 1st Byte
command[6]=(delay&0xff000000)>>24;
// Delay Count 2nd Byte
command[7]=(delay&0xff0000)>>16;
// Delay Count 3rd Byte
command[8]=(delay&0xff00)>>8;
// Delay Count 4th Byte
command[9]=(delay&0xff);
data.append((char*)command,command_length);
}
};
int main() {
try {
SpinCorePulseBlaster24Bit p;
std::string program;
p.append_command(program,1,SpinCorePulseBlaster::LOOP,5,2e-6);
p.append_command(program,3,SpinCorePulseBlaster::END_LOOP,0,1e-6);
p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,1e-6);
p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,1e0);
p.append_command(program,1,SpinCorePulseBlaster::STOP,0,1e-6);
p.set_program(program);
p.set_initialized();
p.start();
}
catch(SpinCorePulseBlaster_error e) {
fprintf(stderr, "%s\n", e.c_str());
}
return 0;
}

View File

@ -0,0 +1,23 @@
CXXFLAGS=-g -O0 -Wshadow -Wall -pedantic
CXXCPPFLAGS=-I../..
CXX=g++
all: SpinCore-PulseBlaster24Bit.o
test: test.cpp SpinCore-PulseBlaster24Bit.o ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o ../SpinCore-PulseBlaster/PulseBlasterProgram.o ../../core/core.a
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $^ -lm -lexpat -lxerces-c
../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o: ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h
$(MAKE) -C ../SpinCore-PulseBlaster SpinCore-PulseBlaster.o
../SpinCore-PulseBlaster/PulseBlasterProgram.o: ../SpinCore-PulseBlaster/PulseBlasterProgram.cpp ../SpinCore-PulseBlaster/PulseBlasterProgram.h
$(MAKE) -C ../SpinCore-PulseBlaster PulseBlasterProgram.o
../../core/core.a:
$(MAKE) -C ../../core core.a
SpinCore-PulseBlaster24Bit.o: SpinCore-PulseBlaster24Bit.cpp SpinCore-PulseBlaster24Bit.h
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c -o $@ $<
clean:
rm -f *.o *~ test

View File

@ -0,0 +1,274 @@
/* **************************************************************************
Author: Achim Gaedke
Created: March 2005
****************************************************************************/
#include "core/core.h"
#include "SpinCore-PulseBlaster24Bit.h"
#include <cmath>
#include <typeinfo>
#include <iterator>
#ifndef SIZETPRINTFLETTER
# ifndef __LP64__
# define SIZETPRINTFLETTER "u"
# else
# define SIZETPRINTFLETTER "lu"
# endif
#endif
/*********************************************************************
PulseBlaster24BitCommand
*********************************************************************/
PulseBlaster24BitCommand::PulseBlaster24BitCommand(): PulseBlasterCommand() {
}
PulseBlaster24BitCommand::PulseBlaster24BitCommand(const PulseBlaster24BitCommand& orig): PulseBlasterCommand(orig) {}
PulseBlaster24BitCommand::PulseBlaster24BitCommand(PulseBlaster24BitProgram& p): PulseBlasterCommand(p) {}
PulseBlaster24BitCommand::PulseBlaster24BitCommand(PulseBlaster24BitProgram& p,
int _ttls, double _length)
:PulseBlasterCommand(p, _ttls, _length)
{}
int PulseBlaster24BitCommand::write_to_file(FILE* out, size_t indent) const {
if (program==NULL) throw pulse_exception("PulseBlaster24Bit: Command not associated with Program");
fprintf(out,"%s<instruction ttls=\"0x%06x\" ",
std::string(indent,' ').c_str(),ttls);
switch(instruction) {
case SpinCorePulseBlaster::LOOP:
fprintf(out,"inst=\"LOOP\" loops=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::LONG_DELAY:
fprintf(out,"inst=\"LONG_DELAY\" loops=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::BRANCH:
fprintf(out,"inst=\"BRANCH\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::END_LOOP:
fprintf(out,"inst=\"END_LOOP\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::JSR:
fprintf(out,"inst=\"JSR\" address=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::CONTINUE:
fprintf(out,"inst=\"CONTINUE\"");
break;
case SpinCorePulseBlaster::STOP:
fprintf(out,"inst=\"STOP\"");
break;
case SpinCorePulseBlaster::RTS:
fprintf(out,"inst=\"RTS\"");
break;
case SpinCorePulseBlaster::WAIT:
fprintf(out,"inst=\"WAIT\"");
break;
default:
fprintf(out,"inst=\"%d\" instdata=\"UNKNOWN\"",instruction);
}
fprintf(out," length=\"%g\"/>\n",(1.0*length)/program->internal_clock_freq);
return 1;
}
/*********************************************************************
PulseBlaster24BitProgram
*********************************************************************/
PulseBlaster24BitProgram::PulseBlaster24BitProgram(): PulseBlasterProgram() {
internal_clock_freq=100.0e6;
minimum_interval=9;
ttl_device_id=0;
}
PulseBlaster24BitProgram::PulseBlaster24BitProgram(const SpinCorePulseBlaster24Bit& pb24) {
internal_clock_freq=pb24.clock;
minimum_interval=pb24.shortest_pulse;
ttl_device_id=pb24.ttl_device_id;
}
PulseBlaster24BitProgram::PulseBlaster24BitProgram(const PulseBlaster24BitProgram& orig): PulseBlasterProgram() {
ttl_device_id=orig.ttl_device_id;
// be sure, there are no old list items left!
clear();
const_iterator orig_i;
for (orig_i=orig.begin(); orig_i!=orig.end(); ++orig_i) {
const PulseBlaster24BitCommand* the_command=dynamic_cast<const PulseBlaster24BitCommand*>(*orig_i);
if (the_command==NULL) {
throw SpinCorePulseBlaster_error("wrong command class or NULL pointer found in program");
}
PulseBlaster24BitCommand* new_one=new PulseBlaster24BitCommand(*the_command);
new_one->program=this;
push_back(new_one);
}
// set correct references...
orig_i=orig.begin();
for(iterator i=begin(); i!=end(); ++i) {
(**i).program=this;
if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) {
(**i).jump=i;
advance((**i).jump,distance(orig.begin(),(**orig_i).jump)-distance(orig.begin(),orig_i));
}
++orig_i;
}
}
PulseBlasterCommand* PulseBlaster24BitProgram::create_command(const state& the_state) {
if (typeid(the_state)==typeid(state_sequent)||typeid(the_state)==typeid(state_parallel))
throw pulse_exception("PulseBlasterCommands can only be created from states");
long unsigned int interval = ceil(the_state.length*internal_clock_freq);
if ( interval < minimum_interval) {
fprintf(stderr,"PulseBlaster state too short (%lu < %lu ticks): %e\n", interval, \
minimum_interval, \
the_state.length);
throw pulse_exception("PulseBlaster state too short");
}
// all states or-ed together
int the_ttls=0;
for (state::const_iterator i=the_state.begin(); i!=the_state.end(); ++i) {
const ttlout* to=dynamic_cast<const ttlout*>(*i);
if (to!=NULL && to->id==ttl_device_id) {
the_ttls|=to->ttls.to_ulong();
}
}
return new PulseBlaster24BitCommand(*this, the_ttls, the_state.length);
}
PulseBlasterCommand* PulseBlaster24BitProgram::create_command(const PulseBlasterCommand* orig) {
PulseBlaster24BitCommand* new_command=NULL;
if (orig==NULL) {
new_command=new PulseBlaster24BitCommand();
new_command->program=this;
new_command->length=minimum_interval;
}
else {
const PulseBlaster24BitCommand* command24Bit=dynamic_cast<const PulseBlaster24BitCommand*>(orig);
if (command24Bit==NULL) throw pulse_exception("wrong PulseBlasterCommand class");
new_command=new PulseBlaster24BitCommand(*command24Bit);
}
return new_command;
}
int PulseBlaster24BitProgram::write_to_file(FILE* out, size_t indent) const {
std::string indent_string(indent,' ');
fprintf(out,"%s<PulseBlaster24BitProgram>\n",indent_string.c_str());
for(const_iterator i=begin(); i!=end(); ++i) {
if (*i==NULL) throw SpinCorePulseBlaster_error("NULL pointer found in command list");
(**i).write_to_file(out, indent+2);
}
fprintf(out,"%s</PulseBlaster24BitProgram>\n",indent_string.c_str());
return 1;
}
/*********************************************************************
SpinCorePulseBlaster24Bit
*********************************************************************/
void SpinCorePulseBlaster24Bit::write_command(unsigned char* data, int flags, opcode inst, unsigned int inst_data, size_t delay) {
if (inst>8)
throw SpinCorePulseBlaster_error("instruction code not known");
// Output, Control Word 1st Byte
data[0]=(flags&0xff0000)>>16;
// Output, Control Word 2nd Byte
data[1]=(flags&0xff00)>>8;
// Output, Control Word 3rd Byte
data[2]=flags&0xff;
// Data Field 1st Byte
data[3]=(inst_data&0x0ff000)>>12;
// Data Field 2nd Byte
data[4]=(inst_data&0xff0)>>4;
// Data Field 3rd Byte and opcode
data[5]=(inst_data&0xf)<<4|(inst&0xf);
// Delay Count 1st Byte
data[6]=(delay&0xff000000)>>24;
// Delay Count 2nd Byte
data[7]=(delay&0xff0000)>>16;
// Delay Count 3rd Byte
data[8]=(delay&0xff00)>>8;
// Delay Count 4th Byte
data[9]=(delay&0xff);
/* *** BUG-FIX (ToDo: clean solution) ***
there is a nasty error in pulseblaster, affecting all states with 4th byte
equal 0xff and delay >255. In this case reduce state for 10ns.
*/
if (0 && data[9]==0xff && delay>0xff)
data[9]=0xfe;
}
void SpinCorePulseBlaster24Bit::write_command(unsigned char* data, const PulseBlasterCommand& command) {
const PulseBlaster24BitCommand* command24Bit=dynamic_cast<const PulseBlaster24BitCommand*>(&command);
if (command24Bit==NULL)
throw SpinCorePulseBlaster_error("found wrong command class in PulseBlasterProgram");
if (command24Bit->program==NULL) throw SpinCorePulseBlaster_error("Command not associated with Program");
unsigned int inst_data=0;
switch(command24Bit->instruction) {
case SpinCorePulseBlaster::CONTINUE:
case SpinCorePulseBlaster::STOP:
case SpinCorePulseBlaster::WAIT:
case SpinCorePulseBlaster::RTS:
// no parameter
break;
case SpinCorePulseBlaster::LOOP:
inst_data=command24Bit->loop_count-1;
break;
case SpinCorePulseBlaster::LONG_DELAY:
inst_data=command24Bit->loop_count-2;
break;
case SpinCorePulseBlaster::BRANCH:
case SpinCorePulseBlaster::END_LOOP:
case SpinCorePulseBlaster::JSR:
inst_data=std::distance(command24Bit->program->begin(),command24Bit->jump);
break;
default:
throw SpinCorePulseBlaster_error("instruction code not known");
}
if (command24Bit->length<3)
throw SpinCorePulseBlaster_error("delay length too small!");
unsigned int delay=(unsigned int)command24Bit->length-3;
write_command(data,command24Bit->ttls,command24Bit->instruction,inst_data,delay);
}
void SpinCorePulseBlaster24Bit::write_to_device(const PulseBlaster24BitProgram& p) {
std::string program;
//Begin pulse program
for (PulseBlaster24BitProgram::const_iterator c=p.begin(); c!=p.end(); ++c) {
char command[10];
write_command((unsigned char*)command,**c);
program.append(command, (size_t)10);
}
set_program(program);
// End of programming registers and pulse program
set_initialized();
}
PulseBlasterProgram* SpinCorePulseBlaster24Bit::create_program(state& exp) {
PulseBlaster24BitProgram* prog=new PulseBlaster24BitProgram();
// the user's code
prog->append_sequence(exp);
return prog;
}
void SpinCorePulseBlaster24Bit::run_pulse_program(const PulseBlasterProgram& p) {
const PulseBlaster24BitProgram* prog=dynamic_cast<const PulseBlaster24BitProgram*>(&p);
if (prog==NULL)
throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlaster24Bit method");
write_to_device(*prog);
start();
}

View File

@ -0,0 +1,143 @@
/* **************************************************************************
Author: Achim Gaedke
Created: March 2005
****************************************************************************/
#ifndef SPINCOREPULSEBLASTER24BIT_H
#define SPINCOREPULSEBLASTER24BIT_H
#ifndef SPINCORE_PULSEBLASTER_H
#include "drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h"
#include "drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h"
#endif
#include "core/stopwatch.h"
#include <cstdio>
#include <string>
#include <vector>
#include <list>
#include <cmath>
#include <unistd.h>
/**
\defgroup SpinCorePulseBlaster24Bit SpinCore PulseBlaster 24Bit
\ingroup drivers
\brief classes concerning SpinCore PulseBlaster 24Bit version
@{
*/
class SpinCorePulseBlaster24Bit;
class PulseBlaster24BitCommand;
/**
\brief holds the complete and detailed initialisation of the device
*/
class PulseBlaster24BitProgram: public PulseBlasterProgram {
public:
/// ttl device id
int ttl_device_id;
/// standard allocator
PulseBlaster24BitProgram();
/// copy constructor has to correct the pointers and references in the command list
PulseBlaster24BitProgram(const PulseBlaster24BitProgram& orig);
/// create suitable empty program
PulseBlaster24BitProgram(const SpinCorePulseBlaster24Bit& pb24);
/// write all configuration to a file to xml
int write_to_file(FILE* out, size_t indent=0) const;
/// create a suitable state for this kind of program
virtual PulseBlasterCommand* create_command(const state& state);
/// create a copy or a minimal empty state
virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* orig=NULL);
};
/**
\brief parameters for each PulseBlaster command
*/
class PulseBlaster24BitCommand: public PulseBlasterCommand {
public:
/// the simple constructor
PulseBlaster24BitCommand();
/// constructs a minimal length state
PulseBlaster24BitCommand(PulseBlaster24BitProgram&);
/// the copy constructor
PulseBlaster24BitCommand(const PulseBlaster24BitCommand&);
// a useful constructor for a simple CONTINUE state
PulseBlaster24BitCommand(PulseBlaster24BitProgram& p, int _ttls, double _length);
/// write the command to a given file as xml tag
int write_to_file(FILE* out, size_t indent=0) const;
};
/**
\brief the driver implementation for PulseBlaster24Bit
uses the classes PulseBlaster24BitProgram and PulseBlaster24BitCommand
*/
class SpinCorePulseBlaster24Bit: public SpinCorePulseBlaster {
public:
/**
initialises the PulseBlaster24Bit of Spincore
*/
SpinCorePulseBlaster24Bit(int the_id=0, double the_clock=1e8, unsigned int _sync_mask=0): SpinCorePulseBlaster(10,the_clock) {
ttl_device_id=the_id;
sync_mask=_sync_mask;
fprintf(stderr, "SpinCore PulseBlaster 24Bit: clock=%f Hz, device id=%d", the_clock, the_id);
if (sync_mask!=0)
fprintf(stderr, ", sync_mask=0x%06x", sync_mask);
fprintf(stderr, "\n");
}
SpinCorePulseBlaster24Bit(const SpinCorePulseBlaster24Bit& orig);
/**
create a full program from state tree
in contrast to append_sequence, this function expects a complete program, not only a fraction, needed by run_pulse_program method
*/
virtual PulseBlasterProgram* create_program(state& exp);
/**
run a PulseBlaster Program
*/
virtual void run_pulse_program(const PulseBlasterProgram& p);
/// write command as a 10 byte machine word
virtual void write_command(unsigned char* data, const PulseBlasterCommand& command);
/// write raw parameters as a 10 byte machine word
virtual void write_command(unsigned char* data, int flags, opcode inst, unsigned int inst_data, size_t delay);
/// append raw parameters as a 10 byte machine word
virtual void append_command(std::string& data, int flags, opcode inst, int inst_data, size_t delay) {
unsigned char buffer[10];
write_command(buffer,flags,inst,inst_data,delay);
data.append((char*)buffer,(size_t)10);
}
/// append command as a 10 byte machine word
virtual void append_command(std::string& data, const PulseBlasterCommand& command) {
unsigned char buffer[10];
write_command(buffer,command);
data.append((char*)buffer,(size_t)10);
}
virtual void write_to_device(const PulseBlaster24BitProgram& p);
/**
destructor (close device)
*/
virtual ~SpinCorePulseBlaster24Bit() {}
};
/**
@}
*/
#endif

View File

@ -0,0 +1,172 @@
#include "SpinCore-PulseBlaster24Bit.h"
#include "core/xml_states.h"
#include <cstring>
#include <cerrno>
int lowlevel_test() {
try {
SpinCorePulseBlaster24Bit p;
std::string program;
p.append_command(program,1,SpinCorePulseBlaster::LOOP,5,97);
p.append_command(program,3,SpinCorePulseBlaster::END_LOOP,0,97);
p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,97);
p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,100000000-3);
p.append_command(program,1,SpinCorePulseBlaster::STOP,0,97);
p.set_program(program);
p.set_initialized();
p.start();
}
catch(SpinCorePulseBlaster_error e) {
fprintf(stderr, "%s\n", e.c_str());
}
return 0;
}
int wait_test() {
try {
SpinCorePulseBlaster24Bit p;
std::string program;
//virtual void append_command(std::string& data, int flags, opcode inst, int inst_data, size_t delay)
p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,97);
p.append_command(program,8,SpinCorePulseBlaster::WAIT,0,97);
p.append_command(program,4,SpinCorePulseBlaster::CONTINUE,0,97);
p.append_command(program,0,SpinCorePulseBlaster::CONTINUE,0,97);
p.append_command(program,0,SpinCorePulseBlaster::STOP,0,97);
p.set_program(program);
p.set_initialized();
p.start();
}
catch(SpinCorePulseBlaster_error e) {
fprintf(stderr, "%s\n", e.c_str());
}
return 0;
}
int simple_sequence() {
try {
SpinCorePulseBlaster24Bit sp24;
PulseBlaster24BitProgram prog(sp24);
// zero
prog.push_back(prog.create_command());
prog.back()->ttls=1;
prog.back()->instruction=SpinCorePulseBlaster::LOOP;
prog.back()->loop_count=10;
prog.back()->length=20;
// one
prog.push_back(prog.create_command());
prog.back()->ttls=0;
prog.back()->instruction=SpinCorePulseBlaster::END_LOOP;
prog.back()->jump=prog.begin();
prog.back()->length=10;
// two
prog.push_back(prog.create_command());
prog.back()->ttls=0;
prog.back()->instruction=SpinCorePulseBlaster::STOP;
sp24.run_pulse_program(prog);
PulseBlaster24BitProgram prog2(prog);
prog.write_to_file(stdout);
sp24.run_pulse_program(prog2);
}
catch(SpinCorePulseBlaster_error e) {
fprintf(stderr, "pulseblaster: %s\n", e.c_str());
return 1;
}
catch(pulse_exception e) {
fprintf(stderr, "pulse: %s\n", e.c_str());
return 1;
}
return 0;
}
class core {
public:
static int term_signal;
};
int state_sequence(const char* filename) {
try {
SpinCorePulseBlaster24Bit sp24;
PulseBlaster24BitProgram prog;
state_atom* sa=xml_state_reader().read_from_file(filename);
state_sequent* ss=dynamic_cast<state_sequent*>(sa);
if (ss==NULL) {
delete sa;
throw SpinCorePulseBlaster_error("found no states");
}
prog.push_front(prog.create_command());
prog.append_sequence(*ss);
prog.push_back(prog.create_command());
prog.push_back(prog.create_command());
prog.back()->instruction=SpinCorePulseBlaster::STOP;
prog.write_to_file(stdout);
state_iterator i(*ss);
while (!i.is_last()) i.next_state();
sp24.duration=i.get_time()+2*90e-9;
sp24.run_pulse_program(prog);
sp24.time_running.start();
sp24.wait_till_end();
fprintf(stdout, "%g seconds elapsed\n", sp24.time_running.elapsed());
}
catch(SpinCorePulseBlaster_error e) {
fprintf(stderr, "pulseblaster: %s\n", e.c_str());
return 1;
}
catch(pulse_exception e) {
fprintf(stderr, "pulse: %s\n", e.c_str());
return 1;
}
return 0;
}
int main(int argc, char** argv) {
core::term_signal=0;
size_t n=1;
const char* filename=NULL;
if (argc<2 || argc>3) {
fprintf(stderr,"%s [repeat] pulsesequence\n",argv[0]);
return 1;
}
if (argc==3) {
char* endptr=NULL;
n=strtoul(argv[1],&endptr,10);
if (*endptr!=0 && n<1) {
fprintf(stderr,"%s [repeat] pulsesequence\n",argv[0]);
return 1;
}
filename=argv[2];
}
if (argc==2) {
filename=argv[1];
}
if (0!=access(filename, R_OK)) {
fprintf(stderr, "%s: could not open file %s, reason: %s\n",argv[0],filename, strerror(errno));
return 1;
}
int value_returned=0;
size_t i=0;
while (i<n && value_returned==0) {
value_returned=state_sequence(filename);
i++;
}
if (n>1) {
fprintf(stdout,"%s: executed %s %u times\n",argv[0],filename,i);
}
return value_returned;
}

View File

@ -0,0 +1,35 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
CXX=g++
CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I../.. -I.
.PHONY: all clean intall
all: SpinCore-PulseBlasterDDSIII.o
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o: ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.cpp ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h
$(MAKE) -C ../SpinCore-PulseBlaster SpinCore-PulseBlaster.o
SpinCore-PulseBlasterDDSIII.o: SpinCore-PulseBlasterDDSIII.cpp SpinCore-PulseBlasterDDSIII.h ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.h ../pulsegen.h ../frequgen.h
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -c $< -o $@
../../core/core.a:
make -C ../../core core.a
test.exe: test.cpp SpinCore-PulseBlasterDDSIII.o ../SpinCore-PulseBlaster/SpinCore-PulseBlaster.o ../SpinCore-PulseBlaster/PulseBlasterProgram.o ../../core/core.a
$(CXX) $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $^ -lexpat
clean: ../../tools/add_endline.exe
for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \
rm -f *.stackdump *.o *.exe \#* *~
install:
install PBD03PC.dll $(PREFIX)

View File

@ -0,0 +1,510 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "SpinCore-PulseBlasterDDSIII.h"
#include <cmath>
#include <typeinfo>
#include <iterator>
#include "core/core.h"
#include "core/states.h"
/*********************************************************************
PulseBlasterDDSIIICommand
*********************************************************************/
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand() {
instruction=SpinCorePulseBlaster::CONTINUE;
length=0;
rx_phase_reg=0;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
tx_phase_reg=0;
tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
freq_reg=0;
ttls=0;
program=NULL;
}
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(const PulseBlasterDDSIIICommand& orig): PulseBlasterCommand(orig) {
rx_phase_reg=orig.rx_phase_reg;
rx_enable=orig.rx_enable;
tx_phase_reg=orig.tx_phase_reg;
tx_enable=orig.tx_enable;
freq_reg=orig.freq_reg;
}
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(PulseBlasterDDSIIIProgram& p,
double _frequency,
double _rx_phase, SpinCorePulseBlasterDDSIII::analog_state _rx_enable,
double _tx_phase, SpinCorePulseBlasterDDSIII::analog_state _tx_enable,
int _ttls, double _length)
:PulseBlasterCommand(p,_ttls,_length) {
rx_phase_reg=0;
tx_phase_reg=0;
freq_reg=0;
// allocate frequency and phases only if necessary
if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON || _tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) {
freq_reg=p.get_frequency_regno(_frequency);
// todo: be sure, whether tx_phase is really needed for DAC_OUT_0
// and so on...
//if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
rx_phase_reg=p.get_rx_phase_regno(_rx_phase);
//if (_tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
tx_phase_reg=p.get_tx_phase_regno(_tx_phase);
}
rx_enable=_rx_enable;
tx_enable=_tx_enable;
}
int PulseBlasterDDSIIICommand::write_to_file(FILE* out, size_t indent) const {
if (program==NULL) throw pulse_exception("PulseBlasterDDSIII: Command not associated with Program");
fprintf(out,"%s<instruction frequency=\"%d\" rxphase=\"%d\" rxenable=\"%d\" txphase=\"%d\" txenable=\"%d\" ttls=\"%d\" ",
std::string(indent,' ').c_str(),freq_reg,rx_phase_reg,rx_enable,tx_phase_reg,tx_enable,ttls);
switch(instruction) {
case SpinCorePulseBlaster::LOOP:
fprintf(out,"inst=\"LOOP\" instdata=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::LONG_DELAY:
fprintf(out,"inst=\"LONG_DELAY\" instdata=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::BRANCH:
fprintf(out,"inst=\"BRANCH\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::END_LOOP:
fprintf(out,"inst=\"END_LOOP\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::JSR:
fprintf(out,"inst=\"JSR\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::CONTINUE:
fprintf(out,"inst=\"CONTINUE\"");
break;
case SpinCorePulseBlaster::STOP:
fprintf(out,"inst=\"STOP\"");
break;
case SpinCorePulseBlaster::RTS:
fprintf(out,"inst=\"RTS\"");
break;
case SpinCorePulseBlaster::WAIT:
fprintf(out,"inst=\"WAIT\"");
break;
default:
fprintf(out,"inst=\"%d\" instdata=\"UNKNOWN\"",instruction);
}
fprintf(out," length=\"%g\"/>\n",1.0/program->internal_clock_freq*length);
return 1;
}
/*********************************************************************
PulseBlasterDDSIIIProgram
*********************************************************************/
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram() {
internal_clock_freq=100.0e6; // Hz
phase_accuracy=360.0/pow(2,12); // degree, manual page 8
freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8
minimum_interval=9; // in clock cycles
}
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const SpinCorePulseBlasterDDSIII& pbddsiii) {
internal_clock_freq=pbddsiii.clock; // Hz
minimum_interval=pbddsiii.shortest_pulse; // in clock cycles
phase_accuracy=360.0/pow(2,12); // degree, manual page 8
freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8
}
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const PulseBlasterDDSIIIProgram& orig): PulseBlasterProgram(orig) {
freq_accuracy=orig.freq_accuracy;
phase_accuracy=orig.phase_accuracy;
frequency_registers=orig.frequency_registers;
rx_phase_registers=orig.rx_phase_registers;
clear();
const_iterator orig_i;
for (orig_i=orig.begin(); orig_i!=orig.end(); ++orig_i) {
const PulseBlasterDDSIIICommand* the_command=dynamic_cast<const PulseBlasterDDSIIICommand*>(*orig_i);
if (the_command==NULL) {
throw SpinCorePulseBlaster_error("wrong command class or NULL pointer found in program");
}
PulseBlasterDDSIIICommand* new_one=new PulseBlasterDDSIIICommand(*the_command);
new_one->program=this;
push_back(new_one);
}
// set correct references...
orig_i=orig.begin();
for(iterator i=begin();i!=end();++i) {
(**i).program=this;
if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) {
(**i).jump=i;
advance((**i).jump,distance(orig.begin(),(**orig_i).jump)-distance(orig.begin(),orig_i));
}
++orig_i;
}
}
int PulseBlasterDDSIIIProgram::write_to_file(FILE* out, size_t indent) const {
std::string indent_string(indent,' ');
fprintf(out,"%s<PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
if (!frequency_registers.empty()) {
fprintf(out,"%s <frequencies>",indent_string.c_str());
for (size_t i=0; i<frequency_registers.size(); i++)
fprintf(out," %g",frequency_registers[i]);
fprintf(out," </frequencies>\n");
}
if (!rx_phase_registers.empty()) {
fprintf(out,"%s <rxphases>",indent_string.c_str());
for (size_t i=0;i<rx_phase_registers.size();i++)
fprintf(out," %g",rx_phase_registers[i]);
fprintf(out," </rxphases>\n");
}
if (!tx_phase_registers.empty()) {
fprintf(out,"%s <txphases>",indent_string.c_str());
for (size_t i=0;i<tx_phase_registers.size();i++)
fprintf(out," %g",tx_phase_registers[i]);
fprintf(out," </txphases>\n");
}
for(const_iterator i=begin();i!=end();++i) {
(**i).write_to_file(out,indent+2);
}
fprintf(out,"%s</PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
return 1;
}
int PulseBlasterDDSIIIProgram::get_frequency_regno(double f) {
size_t i=0;
while (i<frequency_registers.size() && fabs(frequency_registers[i]-f)>freq_accuracy) ++i;
if (i==frequency_registers.size())
frequency_registers.push_back(f);
return i;
}
int PulseBlasterDDSIIIProgram::get_rx_phase_regno(double p) {
size_t i=0;
while (i<rx_phase_registers.size() && fabs(rx_phase_registers[i]-p)>phase_accuracy) ++i;
if (i==rx_phase_registers.size())
rx_phase_registers.push_back(p);
return i;
}
int PulseBlasterDDSIIIProgram::get_tx_phase_regno(double p) {
size_t i=0;
while (i<tx_phase_registers.size() && fabs(tx_phase_registers[i]-p)>phase_accuracy) ++i;
if (i==tx_phase_registers.size())
tx_phase_registers.push_back(p);
return i;
}
PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const state& the_state) {
if (typeid(the_state)!=typeid(state))
throw pulse_exception("tried to make a state from a sequence");
// todo: state's defaults
/*
There are three analog outputs on this card, but only two phase registers and two gates:
id=0, DAC_OUT0: influenced by rx1_specified, tx_phase, rx_enable
id=1, DAC_OUT1: influenced by rx2_specified, rx_phase, rx_enable
id=2, DAC_OUT2: influenced by tx_specified, tx_phase, tx_enable
all channels have the same frequency
*/
double length=the_state.length;
double state_frequency=0;
unsigned long ttls=0;
double rx_phase=0;
SpinCorePulseBlasterDDSIII::analog_state rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
double tx_phase=0;
SpinCorePulseBlasterDDSIII::analog_state tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
int id0_specified=0;
int id1_specified=0;
int id2_specified=0;
for (state::const_iterator i=the_state.begin(); i!=the_state.end(); ++i) {
// collect states information
const ttlout* to=dynamic_cast<const ttlout*>(*i);
if (to!=NULL && to->id==0) {
ttls|=to->ttls.to_ulong();
continue;
}
// add frequency information
const analogout* ao=dynamic_cast<const analogout*>(*i);
if (ao!=NULL && ao->id>=0 && ao->id<=2) {
if ((rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF ||
tx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) &&
fabs(state_frequency-ao->frequency)>freq_accuracy)
throw pulse_exception("only one frequency for analog outputs possible");
state_frequency=ao->frequency;
double phase=ao->phase;
if (phase < 0 || phase >= 360.0) {
phase=fmod(phase, 360.0);
if (phase<0) {phase+=360.0;}
}
assert(phase>=0 && phase<360.0);
switch(ao->id) {
case 0:
if (id0_specified) throw pulse_exception("rx channel (DAC_OUT_0) channel already set");
//if (rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) throw pulse_exception("rx channel already set");
// rx is identified with channel 0
if (id2_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of TX (DAC_OUT_2) channel\n");
tx_phase=phase;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id0_specified=1;
break;
case 1:
if (id1_specified) throw pulse_exception("rx channel (DAC_OUT_1) channel already set");
// tx is identified with channel 1
rx_phase=phase;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id1_specified=1;
break;
case 2:
if (id2_specified || tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) throw pulse_exception("tx channel (DAC_OUT_2) already set");
// tx is identified with channel 1
if (id0_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of RX (DAC_OUT_0) channel\n");
tx_phase=phase;
tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id2_specified=1;
break;
}
continue;
}
}
#if SP_DEBUG
fprintf(stderr, "rx phase=%f, tx phase=%f\n",rx_phase, tx_phase);
#endif
if (!id0_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_0\n");
if (!id1_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_1\n");
return new PulseBlasterDDSIIICommand(*this,state_frequency,rx_phase,rx_enable,tx_phase,tx_enable,ttls,length);
}
PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const PulseBlasterCommand* orig) {
PulseBlasterDDSIIICommand* new_command=NULL;
if (orig==NULL) {
new_command=new PulseBlasterDDSIIICommand();
new_command->program=this;
new_command->length=minimum_interval;
}
else {
const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast<const PulseBlasterDDSIIICommand*>(orig);
if (commandDDSIII==NULL) throw pulse_exception("wrong PulseBlasterCommand class in PulseBlasterDDSIIIProgram method");
new_command=new PulseBlasterDDSIIICommand(*commandDDSIII);
}
return new_command;
}
/*********************************************************************
SpinCorePulseBlasterDDSIII
*********************************************************************/
SpinCorePulseBlasterDDSIII::SpinCorePulseBlasterDDSIII(int the_id, double the_clock, unsigned int _sync_mask): SpinCorePulseBlaster(10,the_clock) {
sync_mask=_sync_mask;
ttl_device_id=the_id;
freq_regno=16;
phase_regno=16;
}
void SpinCorePulseBlasterDDSIII::set_registers(int device, unsigned int register_size, double multiplier, const std::vector<double>& values) {
if (values.size()>register_size)
throw SpinCorePulseBlaster_error("to many data for registers");
unsigned char* data=(unsigned char*)malloc(4*register_size);
if (data==NULL) {
throw SpinCorePulseBlaster_error("could not allocate memory for register data");
}
for (unsigned int reg=0; reg<register_size; ++reg) {
if (values.size()>reg) {
double temp=values[reg]*multiplier;
if (temp<0 || temp>pow(2,32))
throw SpinCorePulseBlaster_error("invalid data value");
unsigned int val=(unsigned int)temp;
data[reg*4]=(val&0xff000000)>>24;
data[reg*4+1]=(val&0xff0000)>>16;
data[reg*4+2]=(val&0xff00)>>8;
data[reg*4+3]=val&0xff;
}
else {
data[reg*4]=0;
data[reg*4+1]=0;
data[reg*4+2]=0;
data[reg*4+3]=0;
}
}
write_register(0,0); // dev reset
write_register(2,4); // bytes per word
write_register(3,device); // dev to program
write_register(4,0); //reset address counter
write_data(data,4*register_size);
free(data);
}
void SpinCorePulseBlasterDDSIII::set_phase_registers(std::vector<double> rx_phases, std::vector<double> tx_phases) {
if (tx_phases.empty())
set_registers(2,phase_regno,1.0,std::vector<double>(1,0.0));
else
set_registers(2,phase_regno,pow(2,32)/360.0,tx_phases);
if (rx_phases.empty())
set_registers(3,phase_regno,1.0,std::vector<double>(1,0.0));
else
set_registers(3,phase_regno,pow(2,32)/360.0,rx_phases);
}
void SpinCorePulseBlasterDDSIII::set_frequency_registers(const std::vector<double>& values) {
if (values.empty())
set_registers(1,freq_regno,pow(2,32)/clock,std::vector<double>(1,1e6));
else
set_registers(1,freq_regno,pow(2,32)/clock,values);
}
void SpinCorePulseBlasterDDSIII::write_command(unsigned char* data, const PulseBlasterCommand& command) {
//void PulseBlasterDDSIII::append_command(std::string& data, int freqreg, int phasereg_tx, int output_tx, int phasereg_rx, int output_rx, int flags, opcode inst, int inst_data, double length)
const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast<const PulseBlasterDDSIIICommand*>(&command);
if (commandDDSIII==NULL)
throw SpinCorePulseBlaster_error("found wrong command class in PulseBlasterProgram");
if (command.program==NULL) throw SpinCorePulseBlaster_error("Command not associated with Program");
int inst_data=0;
switch(command.instruction) {
case SpinCorePulseBlaster::CONTINUE:
case SpinCorePulseBlaster::STOP:
case SpinCorePulseBlaster::WAIT:
case SpinCorePulseBlaster::RTS:
// no parameter
break;
case SpinCorePulseBlaster::LOOP:
inst_data=command.loop_count-1;
break;
case SpinCorePulseBlaster::LONG_DELAY:
inst_data=command.loop_count-2;
break;
case SpinCorePulseBlaster::BRANCH:
case SpinCorePulseBlaster::END_LOOP:
case SpinCorePulseBlaster::JSR:
inst_data=std::distance(command.program->begin(),command.jump);
break;
default:
throw SpinCorePulseBlaster_error("instruction code not known");
}
unsigned int delay=(unsigned int)command.length-3;
// Output, Control Word 1st Byte
data[0]=(commandDDSIII->freq_reg&0x0f)<<4|(commandDDSIII->tx_phase_reg&0x0f);
// Output, Control Word 2nd Byte
data[1]=((commandDDSIII->rx_phase_reg&0x0f)<<4)|((command.ttls&0x300)>>8);
if (commandDDSIII->rx_enable==ANALOG_OFF) data[1]|=0x04;
if (commandDDSIII->tx_enable==ANALOG_OFF) data[1]|=0x08;
// Output, Control Word 3rd Byte
data[2]=command.ttls&0xff;
// Data Field 1st Byte
data[3]=(inst_data&0x0ff000)>>12;
// Data Field 2nd Byte
data[4]=(inst_data&0xff0)>>4;
// Data Field 3rd Byte and opcode
data[5]=(inst_data&0xf)<<4|(command.instruction&0xf);
// Delay Count 1st Byte
data[6]=(delay&0xff000000)>>24;
// Delay Count 2nd Byte
data[7]=(delay&0xff0000)>>16;
// Delay Count 3rd Byte
data[8]=(delay&0xff00)>>8;
// Delay Count 4th Byte
data[9]=(delay&0xff);
/* *** BUG-FIX (ToDo: clean solution) ***
there is a nasty error in pulseblaster, affecting all states with 4th byte
equal 0xff and delay >255. In this case reduce state for 10ns.
*/
if (data[9]==0xff && delay>0xff)
data[9]=0xfe;
}
int SpinCorePulseBlasterDDSIII::write_to_device(const PulseBlasterDDSIIIProgram& p) {
set_frequency_registers(p.frequency_registers);
set_phase_registers(p.rx_phase_registers,p.tx_phase_registers);
std::string program;
//Begin pulse program
for (PulseBlasterDDSIIIProgram::const_iterator c=p.begin(); c!=p.end();++c) {
char command[10];
write_command((unsigned char*)command,**c);
program.append(command, (size_t)10);
}
set_program(program);
// End of programming registers and pulse program
set_initialized();
return 1;
}
PulseBlasterProgram* SpinCorePulseBlasterDDSIII::create_program(state& exp) {
PulseBlasterDDSIIIProgram* prog=new PulseBlasterDDSIIIProgram();
// some initialisiation...
prog->append_sequence(exp);
// some reset code ...
return prog;
}
void SpinCorePulseBlasterDDSIII::run_pulse_program(const PulseBlasterProgram& p) {
const PulseBlasterDDSIIIProgram* prog=dynamic_cast<const PulseBlasterDDSIIIProgram*>(&p);
if (prog==NULL)
throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlasterDDSIII method");
write_to_device(*prog);
start();
}
void SpinCorePulseBlasterDDSIII::wait_till_end() {
double waittime=duration-time_running.elapsed();
double timeout=(waittime>10)?(waittime*0.01):0.05;
#if SP_DEBUG
fprintf(stderr,"waiting while DDSIII pulseprogram running (%f s of %f s)...", waittime, duration);
#endif
while (waittime>-timeout && core::term_signal==0) {
if (waittime<1e-2)
waittime=1e-2;
else
waittime*=0.9;
#if SP_DEBUG
fprintf(stderr,"sleeping for %g seconds...",waittime);
fflush(stderr);
#endif
timespec nanosleep_time;
nanosleep_time.tv_sec=(time_t)floor(waittime);
nanosleep_time.tv_nsec=(long)ceil((waittime-nanosleep_time.tv_sec)*1e9);
nanosleep(&nanosleep_time,NULL);
waittime=duration-time_running.elapsed();
}
if (core::term_signal!=0) {
//reset pulseblaster
stop();
reset_flags(0);
}
#if SP_DEBUG
fprintf(stderr,"done\n");
#endif
}

View File

@ -0,0 +1,202 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#ifndef SPINCOREPULSEBLASTERDDSIII_H
#define SPINCOREPULSEBLASTERDDSIII_H
#include "drivers/frequgen.h"
#ifndef SPINCORE_PULSEBLASTER_H
#include "drivers/SpinCore-PulseBlaster/SpinCore-PulseBlaster.h"
#include "drivers/SpinCore-PulseBlaster/PulseBlasterProgram.h"
#endif
#include "core/states.h"
#include "core/stopwatch.h"
#include <cstdio>
#include <string>
#include <vector>
#include <list>
#include <cmath>
#include <unistd.h>
/**
\defgroup SpinCorePulseblasterDDSIII SpinCore PulseblasterDDSIII
\ingroup drivers
\brief classes concerning SpinCore PulseBlasterDDSIII
@{
*/
class PulseBlasterDDSIIIProgram;
class PulseBlasterDDSIIICommand;
class SpinCorePulseBlasterDDSIII: public frequgen, public SpinCorePulseBlaster {
/**
number of frequency registers
*/
unsigned int freq_regno;
/**
number of phase registers
*/
unsigned int phase_regno;
void set_registers(int device, unsigned int register_size, double divider, const std::vector<double>& values);
void set_phase_registers(std::vector<double> rx_phases, std::vector<double> tx_phases);
void set_frequency_registers(const std::vector<double>& values);
/**
makes a machine command word from PulseBlasterDDSIIICommand object
*/
void write_command(unsigned char* data, const PulseBlasterCommand& command);
/**
send the entire program to the device
*/
int write_to_device(const PulseBlasterDDSIIIProgram& program);
public:
typedef enum {ANALOG_OFF=0, ANALOG_ON=1} analog_state;
analog_state rx_analog;
analog_state tx_analog;
/**
initialises the PulseblasterDDSIII of Spincore
*/
SpinCorePulseBlasterDDSIII(int the_id=0, double the_clock=1e8, unsigned int _sync_mask=0);
/**
sets the frequency for simple frequency generator use
if set, by default the analog output is set to rx and tx channel with 90 degree shift
*/
virtual void set_frequency(state& s) {frequency=10e6;}
/**
sets the frequency for simple frequency generator use
if set, by default the analog output is set to rx and tx channel with 90 degree shift
*/
virtual void set_frequency(double f) {frequency=f;}
/**
\brief programm the pulseblaster with the subset of states known to him
the pulse program is ended with a wait command, that resets all channels and a stop command that stops the pulseblaster in this state
*/
virtual PulseBlasterProgram* create_program(state& the_states);
/**
run a PulseBlaster Program
*/
virtual void run_pulse_program(const PulseBlasterProgram& p);
/**
provide fake substitution for get_status
*/
inline int get_status() {
fprintf(stderr,"PulseBlaster DDSIII does not support status readback! (assuming running state)\n");
return 4;
}
/**
status queries not working, workaround
*/
void wait_till_end();
/**
destructor (close device)
*/
virtual ~SpinCorePulseBlasterDDSIII() {};
};
/**
\brief holds the complete and detailed initialisation of the device
*/
class PulseBlasterDDSIIIProgram: public PulseBlasterProgram {
public:
double freq_accuracy;
double phase_accuracy;
/// here keep track of the programed frequencies(Hz)
std::vector<double> frequency_registers;
/// here keep track of the programed RX phases (degree)
std::vector<double> rx_phase_registers;
/// here keep track of the programed TX phases (degree)
std::vector<double> tx_phase_registers;
/// finds or allocates frequency given in Hz
int get_frequency_regno(double f);
/// finds or allocates rx phase given in degree
int get_rx_phase_regno(double p);
/// finds or allocates tx phase given in degree
int get_tx_phase_regno(double p);
/// standard allocator sets only accuracy
PulseBlasterDDSIIIProgram();
/// copy constructor has to correct the pointers and references in the command list
PulseBlasterDDSIIIProgram(const PulseBlasterDDSIIIProgram& orig);
PulseBlasterDDSIIIProgram(const SpinCorePulseBlasterDDSIII& pb);
/**
create a new continue command, that merges all state_atom features
*/
virtual PulseBlasterCommand* create_command(const state& the_state);
/**
create a new continue command by copying or a default one
*/
virtual PulseBlasterCommand* create_command(const PulseBlasterCommand* c=NULL);
/// write all configuration to a file to xml
int write_to_file(FILE* out, size_t indent=0) const;
};
/**
\brief parameters for each pulseblaster command
*/
class PulseBlasterDDSIIICommand: public PulseBlasterCommand {
public:
/// register for frequency
int freq_reg;
/// register for rx phase
int rx_phase_reg;
/// enable rx analog output
SpinCorePulseBlasterDDSIII::analog_state rx_enable;
/// register for tx phase
int tx_phase_reg;
/// enable tx analog output
SpinCorePulseBlasterDDSIII::analog_state tx_enable;
/// full ttl state
/// the simple constructor
PulseBlasterDDSIIICommand();
/// the copy constructor
PulseBlasterDDSIIICommand(const PulseBlasterDDSIIICommand&);
/// produces a simple state
PulseBlasterDDSIIICommand(PulseBlasterDDSIIIProgram& p,
double frequency,
double rx_phase, SpinCorePulseBlasterDDSIII::analog_state rx_enable,
double tx_phase, SpinCorePulseBlasterDDSIII::analog_state tx_enable,
int ttls, double length);
/// write the command to a give file as xml tag
int write_to_file(FILE* out, size_t indent=0) const;
};
/**
\brief the driver implementation for PulseBlasterDDSIII!
uses the classes PulseBlasterDDSIIIProgram and PulseBlasterDDSIIICommand
*/
/**
@}
*/
#endif

View File

@ -0,0 +1,162 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "SpinCore-PulseBlasterDDSIII.h"
#include "core/xml_states.h"
#include <cstdlib>
#include <cstdio>
#include <cmath>
/*
run it once
*/
#if 0
int main(int argc, char** argv) {
if (argc<4) {
fprintf(stderr,"%s frequency_MHz pulselength_microsec pulselength_180deg_microsec [startphase_degree]\n",argv[0]);
return 1;
}
double freq_MHz=atof(argv[1]);
double pulselength_us=atof(argv[2]);
double pulselength_180deg_microsec=atof(argv[3]);
double phase_deg=0;
if (argc>4)
phase_deg=atof(argv[4]);
if (pulselength_us>100) {
fprintf(stderr,"pulselength resticted to 100 microseconds\n");
return 1;
}
SpinCorePulseBlasterPlus pb;
pb.set_frequency(freq_MHz);
pb.single_pulse_and_180deg_program((50.0+M_PI*phase_deg/180.0)/(1.0e6*freq_MHz)-1.0e-5,
pulselength_us*1e-6,
pulselength_180deg_microsec*1e-6,
1e-3);
return 0;
}
#endif
#if 0
int main () {
PulseBlasterPlusProgram p;
PulseBlasterPlusCommand loop(p,1e6,90,0,0,0,0x0,1e-6);
loop.instruction=2;
loop.loop_count=2;
p.push_back(loop);
p.push_back(PulseBlasterPlusCommand(p,1e6,90,0,0,0,255,1e-6));
PulseBlasterPlusCommand c_loop_end(p,1e6,90,0,0,0,0x0,1e-6);
c_loop_end.instruction=3;
c_loop_end.jump=p.begin();
p.push_back(c_loop_end);
PulseBlasterPlusCommand c_stop(p,1e6,90,1,0,1,0x0,1e-6);
c_stop.instruction=1;
p.push_back(c_stop);
p.write_to_file(stdout);
try {
SpinCorePulseBlasterPlus().run_program(p);
}
catch(pulse_exception e) {
fprintf(stderr,e.c_str());
}
return 0;
}
#endif
#if 0
int main() {
state s;
s.length=1e-3;
ttlout* t=new ttlout;
t->ttls=2;
s.push_back(t);
t=new ttlout;
t->ttls=5;
s.push_back(t);
analogout* a=new analogout;
a->id=0;
a->frequency=1e6;
a->phase=90;
s.push_back(a);
a=new analogout;
a->id=1;
a->frequency=1e6;
a->phase=0;
s.push_back(a);
try {
PulseBlasterPlusProgram* p=SpinCorePulseBlasterPlus().create_program(s);
if (p==NULL) {
fprintf(stderr,"got a NULL pointer from SpinCorePulseBlasterPlus::create_program");
return 1;
}
p->write_to_file(stdout);
delete p;
}
catch (pulse_exception e) {
fprintf(stderr,"%s\n",e.c_str());
return 1;
}
return 0;
}
#endif
#if 1
int main(int argc, char** argv) {
if (argc!=2) {
fprintf(stderr,"%s filename",argv[0]);
return 1;
}
std::string filename(argv[1]);
xml_state_reader reader;
state_atom* state_file=reader.read_from_file(filename);
if (state_file==NULL) {
fprintf(stderr,"reading was not successfull\n");
return 1;
}
state* s=dynamic_cast<state*>(state_file);
if (s==NULL) {
fprintf(stderr,"File does not contain a state or state sequence!");
delete state_file;
return 1;
}
try {
SpinCorePulseBlasterDDSIII pb;
PulseBlasterProgram* p=pb.create_program(*s);
if (p==NULL) {
fprintf(stderr,"got a NULL pointer from SpinCorePulseBlasterPlus::create_program");
return 1;
}
delete p;
//p->write_to_file(stdout);
for (int i=0; i<1;++i) {
pb.SpinCorePulseBlaster::run_pulse_program(*s);
pb.wait_till_end();
}
}
catch (pulse_exception e) {
delete s;
fprintf(stderr,"%s\n",e.c_str());
return 1;
}
catch (SpinCorePulseBlaster_error e) {
delete s;
fprintf(stderr,"%s\n",e.c_str());
return 1;
}
delete s;
return 0;
}
#endif

Binary file not shown.

View File

@ -0,0 +1,230 @@
/**
* \file
* \brief Implementation of the Driver for the DAC20
* Digital to Analog Converter Board
*
* This board was first used in the PFG, and gets now
* adopted by the Field-Cycling spectrometers.
*
* The board inverts all digital inputs.
* This driver partially honors this.
* Especially the data input is not yet inverted.
* -# Inverting the data line is equivalent to inverting
* the value and add/substracting one.
* -# There are different versions of the board around,
* where one board "undoes" the inversion by having an
* inverting op-amp after the DAC.
* -# The digital units have to be calibrated to physical
* units anyway. A sign doesn't hurt much.
*
* This is still a current discussion topic and might
* change.
*/
#include "DAC20.h"
#include <cstdio>
#include <cmath>
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
using std::reverse;
using std::cout;
using std::vector;
#ifndef TIMING
#define TIMING 9e-8
#endif
// The bit depth of the DAC
#define DAC_BIT_DEPTH 20
// The channel configuration
#define DATA_BIT 18//18
#define CLK_BIT 16//16
DAC20::DAC20(int myid): id(myid) {
dac_value = 0;
set_latch_bit(17);
}
DAC20::~DAC20() {}
// This sets the dac_value
void DAC20::set_dac(signed dw) {
dac_value = dw;
}
void DAC20::set_latch_bit(int le_bit)
{
latch_bit = le_bit;
}
// This sets the DAC
void DAC20::set_dac(state& experiment) {
state_sequent* exp_sequence=dynamic_cast<state_sequent*>(&experiment);
if (exp_sequence == NULL)
// is a very state on top level, todo: change interface
throw pfg_exception( "cannot work on a single state, sorry (todo: change interface)");
else {
for(state_sequent::iterator child_state = exp_sequence->begin(); child_state != exp_sequence->end(); ++child_state)
set_dac_recursive(*exp_sequence, child_state);
// std::cout << "first state"<< std::endl;
// Set DAC to 0 at start of experiment
set_dac_to_zero(exp_sequence, exp_sequence->begin());
// And at the end of the experiment
set_dac_to_zero(exp_sequence, exp_sequence->end());
}
}
void DAC20::set_dac_to_zero(state_sequent* exp_sequence, state::iterator where)
{
state s(TIMING);
ttlout* le=new ttlout();
le->id=0;
s.push_front(le);
state_sequent* rep_sequence=new state_sequent();
rep_sequence->repeat=DAC_BIT_DEPTH;
le->ttls = 0 + ( 1 << CLK_BIT );
rep_sequence->push_back(s.copy_new());
le->ttls = 0 ;
rep_sequence->push_back(s.copy_new());
exp_sequence->insert(where, rep_sequence);
//read in the word (41st pulse)
le->ttls=0;
exp_sequence->insert(where, s.copy_new());
// 42nd pulse
// // the state should be 2ms long
// s.length = 2e-3-41*TIMING;
le->ttls= ( 1 << latch_bit );
exp_sequence->insert(where, s.copy_new());
}
// This loops recursive through the state tree
void DAC20::set_dac_recursive(state_sequent& the_sequence, state::iterator& the_state) {
state_sequent* a_sequence = dynamic_cast<state_sequent*>(*the_state);
// Am I a sequence? Yes? Go one sequence further
if (a_sequence != NULL) {
for(state_sequent::iterator child_state = a_sequence->begin(); child_state != a_sequence->end(); ++child_state)
set_dac_recursive(*a_sequence, child_state);
}
// I am not a sequence, but a state
else {
state* this_state = dynamic_cast<state*>(*the_state);
if (this_state == NULL)
throw pfg_exception( "state_atom in state_sequent not expected");
analogout* PFG_aout = NULL;
// find an analogout section with suitable id
state::iterator pos = this_state->begin();
while(pos!=this_state->end()) { // state members loop
analogout* aout = dynamic_cast<analogout*>(*pos); // initialize new analogout
// This is for me, analogout is != NULL (there is an analogout) and has my ID
if (aout!=NULL && aout->id == id) {
if (PFG_aout == NULL) {
// save the informations
PFG_aout = aout;
}
// there is no place for me here
else {
throw pfg_exception( "found another DAC section, ignoring");
delete aout;
}
// remove the analog out section
this_state->erase(pos++);
}
else {
++pos;
}
} // state members loop
if (PFG_aout != NULL) { // state modifications
//std::cout<<"found a analog out section, value="<<PFG_aout->dac_value<<std::endl;
// check the length of the state
if (this_state->length < TIMING*41.0)
throw pfg_exception( "time is too short to save DAC information");
else {
// copy of original state
state* register_state = new state(*this_state);
ttlout* register_ttls = new ttlout();
register_ttls->id = 0;
register_state->length = TIMING;
register_state->push_back(register_ttls);
if (PFG_aout->dac_value > (pow(2.0, int(DAC_BIT_DEPTH-1))-1) )
throw pfg_exception("dac_value too high");
if ( abs(PFG_aout->dac_value) > pow(2.0, int(DAC_BIT_DEPTH-1)) )
throw pfg_exception("dac_value too low");
// now, insert the ttl information
vector<int> dac_word;
for (int j = 0; j < DAC_BIT_DEPTH ; j++) {
int bit = PFG_aout->dac_value & 1;
// // invert the bit
// bit = (bit == 0);
dac_word.push_back(bit);
//cout << dac_word[j];
PFG_aout->dac_value >>= 1;
}
// need one clock cycle to read in bit
// latch enable (LE) should always be high while doing so
// except for the last bit
// reverse the bit pattern
reverse(dac_word.begin(), dac_word.end());
// do run length encoding, grouping same bit values in loops
int last_seen_bit=dac_word[0];
int last_seen_bit_count=1;
for (int i = 1; i < DAC_BIT_DEPTH+1; i++) {
if (i==DAC_BIT_DEPTH || last_seen_bit!=dac_word[i]) {
// so we have to write the bits
// either because the previous were different or we are finished
if (last_seen_bit_count>1) {
// insert a loop
state_sequent* rep_sequence=new state_sequent();
rep_sequence->repeat=last_seen_bit_count;
register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit);
rep_sequence->push_back(register_state->copy_new());
register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit);
rep_sequence->push_back(register_state->copy_new());
the_sequence.insert(the_state, rep_sequence);
} else {
// no loop necessary, insert two states
register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << CLK_BIT) + (1 << latch_bit);
the_sequence.insert(the_state, register_state->copy_new());
register_ttls->ttls = (1 << DATA_BIT)*last_seen_bit + (1 << latch_bit);
the_sequence.insert(the_state, register_state->copy_new());
}
// reset counter and bits if we are not finished
if (i<DAC_BIT_DEPTH) {
last_seen_bit=dac_word[i];
last_seen_bit_count=1;
}
} // finished writing
else
last_seen_bit_count+=1; // same bit value, so continue
} // end of bit loop
register_ttls->ttls = 0;
the_sequence.insert(the_state,register_state->copy_new());
// shorten the remaining state
// and add LE high to this state
ttlout* ttls=new ttlout();
// 42nd pulse
this_state->length -= TIMING*41;
ttls->ttls = 1 << latch_bit;
this_state->push_front(ttls);
// cleanup
delete register_state;
delete PFG_aout;
} // state was long enough to work on
}
else {
ttlout* le_ttls=new ttlout();
le_ttls->ttls = 1 << latch_bit;
this_state->push_back(le_ttls);
}
// end of state modifications
} // I was a state
}

View File

@ -0,0 +1,47 @@
/*
Markus Rosenstihl 2005 Nov
*/
#ifndef DAC20_H
#define DAC20_H
#include "core/states.h"
#include "drivers/pfggen.h"
/**
* \ingroup drivers
*/
class DAC20: public GenericDAC {
protected:
/// The channel for the latch signal
int latch_bit;
public:
int id;
// default constructor
DAC20(int myid=0);
virtual void set_dac(signed dw);
// virtual void set_dac_ttls(signed value);
void set_latch_bit(int le_bit);
/**
inserts necessary serial data transmission to set the dac, at the end of experiment reset the dac
assumes, the root sequence is not repeated, because the reset sequence is appended to the root sequence!
*/
virtual void set_dac(state& experiment);
// destructor
virtual ~DAC20();
private:
void set_dac_recursive(state_sequent& the_sequence, state::iterator& the_state);
void set_dac_to_zero(state_sequent* exp_sequence, state::iterator where);
};
/*class pfg_exception: public std::string {
public:
pfg_exception(const std::string& s): std::string(s){}
};
*/
#endif

View File

@ -0,0 +1,34 @@
#include <cstdio>
#include "core/xml_states.h"
#include "drivers/Tecmag-DAC20/DAC20.h"
int main(int argc, char** argv)
{
int return_result=0;
try {
signed dac_value=2;
state_atom* a=xml_state_reader().read_from_file("/dev/stdin");
if (a==NULL) {
fprintf(stderr, "%s: could not read a state tree from stdin\n", argv[0] );
return 1;
}
state* a_state=dynamic_cast<state*>(a);
if (a_state==NULL) {
fprintf(stderr, "%s: did not find a state in input\n", argv[0] );
delete a;
return 1;
}
DAC20().set_dac(dac_value);
DAC20().set_dac(*a_state);
xml_state_writer().write_states(stdout,*a,1);
delete a;
return 0;
}
catch(pfg_exception pfg_e){
fprintf(stderr,"DAC20: %s\n",pfg_e.what());
return_result=1;
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<sequent>
<state t="1">
<ttlout value="1"/>
</state>
<state t="1">
<analogout id="0" dac_value="10" a="5"/>
<ttlout id="0" value="2"/>
</state>
<state t="1">
<analogout id="0" dac_value="20" a="5"/>
<ttlout id="0" value="3"/>
</state>
<state t="1">
<analogout id="0" dac_value="-20" a="5"/>
<ttlout id="0" value="4"/>
</state>
</sequent>

Binary file not shown.

View File

@ -0,0 +1,27 @@
CXX=g++
CXXFLAGS=-g -O0 -Wall -Wshadow -pedantic
CXXCPPFLAGS=-I. -I../..
LDFLAGS=-lexpat -lxerces-c
.PHONY: all clean install
all: DAC20.o DAC_test.exe
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
../../core/core.a:
$(MAKE) -C ../../core core.a
clean: ../../tools/add_endline.exe
for f in DAC20.cpp DAC20.h DAC_test.cpp; do $< $$f; done
rm -f *.exe *.o *~ core.*
DAC20.o: DAC20.cpp DAC20.h
$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $<
DAC_test.o: DAC_test.cpp
$(CXX) -c $(CXXFLAGS) $(CXXCPPFLAGS) -o $@ $<
DAC_test.exe: DAC_test.o DAC20.o ../../core/core.a
$(CXX) -o $@ $^ $(LDFLAGS)

View File

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<sequent>
<state time="9.9999999999999995e-07"><ttlout value="61440"/></state>
<state time="9.9999999999999995e-07"><ttlout value="32768"/></state>
<state time="0.0001"/>
<state time="2e-6"><analogout id="1" f="75000000.000000" phase="0.000000"/></state>
<state time="0.11999599999999999"/>
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
<state time="5.75e-07"><ttlout value="3"/></state>
<state time="2e-6"><analogout id="0" f="0.000000" phase="0.000000" dac_value="10002"/></state>
<state time="2e-6"><analogout id="1" f="0.000000" phase="0.000000"/></state>
<state time="0.00031804892620614084"/>
<state time="3.7e-5"><analogout id="1" a="5" dac_value="200"/></state>
<state time="3.7e-5"><analogout id="0" a="5" dac_value="10002"/></state>
<state time="3.7e-5"><analogout id="1" a="5" dac_value="1"/></state>
<state time="3.7e-5"><analogout id="0" a="5" dac_value="5"/></state>
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
<state time="5.75e-07"><ttlout value="3"/></state>
<state time="2e-6"><analogout id="1" f="0.000000" phase="-123.000000"/></state>
<state time="0.00031033642620614083"/>
<state time="0.00025999999999999998"><analogin s="4096" f="2e+07" sensitivity="1.000000"/></state>
<state time="2e-6"><analogout id="1" f="0.000000" phase="0.000000"/></state>
<state time="0.0094230885737938593"/>
<state time="1.9999999999999999e-06"><ttlout value="1"/></state>
<state time="5.75e-07"><ttlout value="3"/></state>
<state time="2e-6"><analogout id="1" f="0.000000" phase="-123.000000"/></state>
<state time="0.00031062392620614081"/>
<state time="0.00020684799999999998"><analogin s="4096" f="2e+07" sensitivity="1.000000"/></state>
</sequent>

View File

@ -0,0 +1,58 @@
#include <stdio.h>
#include "tiepie.h"
#if 0
# include "tiepie.cpp"
#endif
#include "TiePie-HS3.h"
int test1() {
word retval;
dword serialno;
if (1!=OpenDLL(dtHandyscope3)) {
printf("failed loading dlls\n");
return 1;
}
retval=InitInstrument(0);
if (retval!=NO_ERROR) {
printf("failed to initialize the instrument, code=%d\n", retval);
CloseDLL();
return 1;
}
retval=GetSerialNumber(&serialno);
if (retval!=NO_ERROR) {
printf("could not read serial number, code=%d\n", retval);
ExitInstrument();
CloseDLL();
return 1;
}
printf("serial no %d\n", serialno);
ExitInstrument();
CloseDLL();
return 0;
}
int test2() {
try {
TiePieHS3 adc;
dword serialno;
word retval=GetSerialNumber(&serialno);
if (retval!=NO_ERROR) throw ADC_exception("could not get serialno");
printf("serial number %d\n", serialno);
}
catch (ADC_exception e) {
printf("ADC exception: %s\n", e.c_str());
return 1;
}
return 0;
}
int main() {
return test2();
}

View File

@ -0,0 +1,39 @@
#############################################################################
#
# Author: Achim Gaedke
# Created: June 2004
#
#############################################################################
CXX=g++
CXXFLAGS=-g -O0 -I. -I../..
LDFLAGS=
.PHONY: all clean install
all: TiePie-HS3.a
../../tools/add_endline.exe: ../../tools/add_endline.cpp
$(CXX) $< -o $@
TiePie-HS3.o: TiePie-HS3.cpp TiePie-HS3.h
$(CXX) -c $(CXXFLAGS) $< -o $@
tiepie.o: tiepie.cpp tiepie.h
$(CXX) -c $(CXXFLAGS) $< -o $@
TiePie-HS3.a: TiePie-HS3.o tiepie.o
$(AR) r $@ $^
HS3test.exe: HS3test.cpp TiePie-HS3.a ../../core/core.a
$(CXX) $(CXXFLAGS) $^ -o $@
../../core/core.a:
$(MAKE) -C ../../core core.a
clean:
for f in *.cpp *.h; do ../../tools/add_endline.exe $$f; done; \
rm -f *.o *~ \#* *.stackdump *.a *.exe
install:
install *.hex *.dll $(PREFIX)

View File

@ -0,0 +1,217 @@
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "drivers/TiePie-HS3/TiePie-HS3.h"
#include "tiepie.h"
#include <cmath>
#include <cstdio>
#include "core/xml_states.h"
TiePieHS3::TiePieHS3() {
if (1!=OpenDLL(dtHandyscope3)){
throw ADC_exception("could not find driver dll for HS3!");
}
if (NO_ERROR!=InitInstrument(0)) {
ADC_exception("HS3 not available!");
}
unsigned int serialno=0;
unsigned short int retval=GetSerialNumber(&serialno);
//fprintf(stderr, "sizeof(unsigned int)=%d, sizeof(unsigned short int)=%d\n", sizeof(serialno), sizeof(retval));
if (NO_ERROR!=retval) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "could not get device's serial number: GetSerialNumber returned %d", retval);
throw ADC_exception(std::string(buffer));
}
fprintf(stderr, "successfully initialized HS3, SN=%d\n",serialno);
trigger_line_id=0;
trigger_line_mask=1<<2;
}
void TiePieHS3::sample_after_external_trigger(double _rate, size_t _samples, double _sensitivity ,size_t _resolution) {
ADC_Abort();
/* set autoranging off */
SetAutoRanging(Ch1,0);
SetAutoRanging(Ch2,0);
samples=_samples;
if (samples==0 || _rate<=0) return;
unsigned short int retval;
//samples=1024*1024*1024; // impossible value... provoke error
retval=SetRecordLength(samples);
if (0!=retval) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "could not set record length: SetRecrodLength(%d) returned %d", samples, retval);
throw ADC_exception(std::string(buffer));
}
if (0!=SetPostSamples(samples)){
throw ADC_exception("could not set post sample number");
}
fprintf(stderr,"set sample number to %d\n",samples);
/* set sampling frequency */
unsigned int freq=(unsigned int)fabs(floor(_rate)+0.5);
unsigned int freq_req=freq;
SetSampleFrequency(&freq_req);
if (freq!=freq_req)
throw ADC_exception("requested frequency could not be set");
rate=freq_req;
fprintf(stderr,"set rate to %g\n",rate);
/* set resolution */
if (0!=SetResolution(_resolution))
throw ADC_exception("could not set resolution");
unsigned char resolution_set;
GetResolution(&resolution_set);
if (_resolution!=resolution_set)
throw ADC_exception("requested resolution not supported");
resolution=_resolution;
fprintf(stderr,"set resolution to %d\n",resolution);
#if 0
/* set DC level value to zero */
const double dclevel_req=-2.0;
if (E_NO_ERRORS!=SetDcLevel(1, dclevel_req))
throw ADC_exception("could not set dc level for channel 1");
if (E_NO_ERRORS!=SetDcLevel(2, dclevel_req))
throw ADC_exception("could not set dc level for channel 2");
#endif
/* set input sensitivity for both channels */
double sensitivity_req1=_sensitivity;
SetSensitivity(1,&sensitivity_req1);
double sensitivity_req2=_sensitivity;
SetSensitivity(2,&sensitivity_req2);
if (sensitivity_req1!=_sensitivity || sensitivity_req2!=_sensitivity)
throw ADC_exception("requested sensitivity could not be set");
sensitivity=_sensitivity;
fprintf(stderr,"set sensitivity to %g\n",sensitivity);
/* set input coupling to DC */
if (0!=SetCoupling(Ch1, ctDC) || 0!=SetCoupling(Ch2, ctDC))
throw ADC_exception("could not set coupling to dc");
/* what to measure */
if (E_NO_ERRORS!=SetMeasureMode(mmCh12)) /* Channel 1 and 2 */
throw ADC_exception("could not set measurement mode");
/* the trigger source */
if (E_NO_ERRORS!=SetTriggerSource(tsExternal)) /* external trigger */
throw ADC_exception("could not set trigger source");
/* which slope to trigger */
if (E_NO_ERRORS!=SetTriggerMode(tmRising)) /* 0=Rising slope */
throw ADC_exception("could not set trigger source");
/* set transfer mode */
if (E_NO_ERRORS!=SetTransferMode(tmBlock))
throw ADC_exception("could not set transfer mode");
/* finally start the measurement */
if (0!=ADC_Start())
throw ADC_exception("could not start triggered adc measurement");
fprintf(stderr,"started triggered adc measurement with %d samples, rate=%g, sensitivity=%g, resolution=%d\n",samples,rate,sensitivity,resolution);
}
void TiePieHS3::set_daq(state& exp) {
size_t s=0;
double f=0.0;
size_t res=0;
double sens=0.0;
// todo: exception...
state_iterator si(dynamic_cast<state_sequent&>(exp));
state* a_state=(state*)si.get_state();
while (NULL!=a_state) {
// find a analogin section
std::list<analogin*> inputs;
for(state::iterator i=a_state->begin(); i!=a_state->end(); ++i) {
analogin* input=dynamic_cast<analogin*>(*i);
if (input!=NULL) {
inputs.push_back(input);
// replace this by a trigger pulse
// to do only short one
ttlout* trigger_pulse=new ttlout();
trigger_pulse->ttls=trigger_line_mask;
trigger_pulse->id=trigger_line_id;
*i=trigger_pulse;
}
}
if (!inputs.empty()) {
if (s!=0 || si.get_count()>1)
throw ADC_exception("Sorry, but TiePie Card does not support mutittriggering");
s=inputs.front()->samples;
f=inputs.front()->sample_frequency;
res=inputs.front()->resolution;
sens=inputs.front()->sensitivity;
// to do only one channel
while (!inputs.empty()) {delete inputs.front();inputs.pop_front();}
}
a_state=(state*)si.next_state();
}
//xml_state_writer().write_states(stderr,exp);
fprintf(stderr,"f=%g s=%d sens=%f res=%d\n",f,s,sens,res);
sample_after_external_trigger(f, s, sens, res);
}
result* TiePieHS3::get_samples(double timeout) {
// return new error_result(1,"for some reasons disabled");
if (samples==0 || rate<=0)
return new adc_result(1);
/* allocate necessary buffers */
unsigned short int* data1=(unsigned short int*)calloc(samples,sizeof(unsigned short int));
if (data1==NULL) throw ADC_exception("could not allocate memory for real data");
unsigned short int* data2=(unsigned short int*)calloc(samples,sizeof(unsigned short int));
if (data2==NULL) {
free(data1);
throw ADC_exception("could not allocate memory for imaginary data");
}
const double poll_timestep=5.0e-2;
double poll_time=0.0;
#if 1
fprintf(stderr,"expecting ADC result...");
#endif
while (1!=ADC_Ready()) {
usleep((unsigned int)floor(poll_timestep*1.0e6));
poll_time+=poll_timestep;
if (poll_time>=timeout) throw ADC_exception("ran into timeout!");
#if 1
fprintf(stderr,"waiting...");
fflush(stderr);
#endif
}
#if 1
fprintf(stderr,"done\n");
#endif
if (ADC_GetData(data1, data2)!=0) {
free(data1);
free(data2);
throw ADC_exception("error while fetching data\n");
}
short int* adc_data=(short int*)calloc(2*samples,sizeof(short int));
if (adc_data==NULL) {
free(data1);
free(data2);
throw ADC_exception("could not allocate memory for adc data");
}
for (size_t i=0;i<samples;i++) {
int result=data1[i];
// there is one more positive value than in two's complement, we have to map this to the hightest positive value
if (result==65535) result-=1;
result-=32767;
adc_data[i*2]=result;
result=data2[i];
if (result==65535) result-=1;
result-=32767;
adc_data[i*2+1]=result;
}
free(data1);
free(data2);
return new adc_result(1,samples,adc_data,rate);
}
TiePieHS3::~TiePieHS3() {
ExitInstrument();
}

Some files were not shown because too many files have changed in this diff Show More