2014-06-26 11:10:51 +00:00
|
|
|
/***************************************************************************
|
|
|
|
|
|
|
|
Author: Markus Rosenstihl
|
|
|
|
Created: June 2010
|
|
|
|
|
|
|
|
****************************************************************************/
|
|
|
|
#include "machines/hardware.h"
|
|
|
|
#include "core/core.h"
|
|
|
|
#include "drivers/PTS-Synthesizer/PTS.h"
|
|
|
|
#include "drivers/Spectrum-MI40xxSeries/Spectrum-MI40xxSeries.h"
|
|
|
|
#include "drivers/SpinCore-PulseBlaster24Bit/SpinCore-PulseBlaster24Bit.h"
|
|
|
|
#include <glib.h>
|
|
|
|
|
2014-08-01 14:18:05 +00:00
|
|
|
#include <fstream>
|
|
|
|
|
2014-06-26 11:10:51 +00:00
|
|
|
/**
|
|
|
|
\defgroup General NMR Spectrometer
|
|
|
|
\ingroup machines
|
|
|
|
Uses:
|
|
|
|
\li Spincore Pulseblaster 24 Bit
|
|
|
|
\li Spectrum MI4021 with gated sampling option (PB ref. clock is fed to Ext.Clock)
|
|
|
|
\li Programmed Test Sources PTS frequency synthesizer with phase control
|
|
|
|
This backend uses a configuration file for setting the various lines and configurations.
|
|
|
|
|
|
|
|
\par Starting the hardware
|
|
|
|
This procedure should assure the correct initialisation of the hardware:
|
|
|
|
\li Switch off main switches of SpinCore Pulseblaster and Computer (the main switch of the computer is at the rear)
|
|
|
|
\li Switch on Computer and start Windows or Linux
|
|
|
|
|
|
|
|
@{
|
|
|
|
*/
|
|
|
|
|
|
|
|
class general_hardware: public hardware
|
|
|
|
{
|
|
|
|
|
|
|
|
PTS* my_pts;
|
|
|
|
SpinCorePulseBlaster24Bit* my_pulseblaster;
|
|
|
|
SpectrumMI40xxSeries* my_adc;
|
2017-02-06 21:31:58 +00:00
|
|
|
bool with_sync;
|
2014-06-26 11:10:51 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
general_hardware()
|
|
|
|
{
|
|
|
|
int SYSCONF = 0;
|
|
|
|
int USRCONF = 0;
|
|
|
|
GKeyFile* cfg_file;
|
|
|
|
GError *error = NULL;
|
|
|
|
char cfg_name[512];
|
|
|
|
const gchar* usr_configdir;
|
|
|
|
const gchar* const * sys_configdirs;
|
|
|
|
sys_configdirs = g_get_system_config_dirs();
|
|
|
|
usr_configdir = g_get_user_config_dir();
|
|
|
|
cfg_file = g_key_file_new();
|
|
|
|
for (int i = 0; i < 100; i++)
|
|
|
|
{
|
|
|
|
if (sys_configdirs[i] == NULL)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snprintf(cfg_name, 512, "%s/damaris/backend.conf", sys_configdirs[i]);
|
|
|
|
fprintf(stdout, "reading backend configuration file (system: %i): %s...\n", i, cfg_name);
|
|
|
|
if (!g_key_file_load_from_file(cfg_file, cfg_name, G_KEY_FILE_NONE, &error))
|
|
|
|
{
|
|
|
|
if (error->code != 4)
|
|
|
|
{
|
2017-01-25 15:46:32 +00:00
|
|
|
fprintf(stdout, "%s","found!\n");
|
|
|
|
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "%s", error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
printf("not found!\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
SYSCONF = 1;
|
|
|
|
error = NULL; // reset error
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
snprintf(cfg_name, 512, "%s/damaris/backend.conf", usr_configdir);
|
|
|
|
fprintf(stdout, "reading backend configuration file (user): %s...\n", cfg_name);
|
|
|
|
if (!g_key_file_load_from_file(cfg_file, cfg_name, G_KEY_FILE_NONE, &error))
|
|
|
|
{
|
|
|
|
if (error->code != 4)
|
|
|
|
{
|
|
|
|
fprintf(stdout, "found!\n");
|
2017-01-25 15:46:32 +00:00
|
|
|
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,"%s", error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
printf("not found!\n");
|
|
|
|
error = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
USRCONF = 1;
|
|
|
|
if (!(SYSCONF | USRCONF))
|
|
|
|
throw(core_exception("configuration failed!\n"));
|
2017-02-06 21:31:58 +00:00
|
|
|
|
2014-06-26 11:10:51 +00:00
|
|
|
printf("done!\n");
|
|
|
|
/* configure ADC card */
|
|
|
|
ttlout trigger;
|
|
|
|
trigger.id = g_key_file_get_integer(cfg_file, "ADC", "id", &error);
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
2017-02-06 21:31:58 +00:00
|
|
|
trigger.ttls = (channel_array) (1 << g_key_file_get_integer(cfg_file, "ADC", "trigger_line", &error));
|
2014-06-26 11:10:51 +00:00
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
int ext_reference_clock = (int) g_key_file_get_double(cfg_file, "ADC", "refclock", &error); // 50 MHz from PB24 SP17; defaults to 100MHz (PB24 SP 2)
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
double impedance = g_key_file_get_double(cfg_file, "ADC", "impedance", &error); // Ohm ( or 50 Ohm)
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
|
|
|
|
error = NULL;
|
2017-02-06 21:31:58 +00:00
|
|
|
my_adc = new SpectrumMI40xxSeries(trigger, (float) impedance, ext_reference_clock);
|
2014-06-26 11:10:51 +00:00
|
|
|
|
|
|
|
/* configure PulseBlaster */
|
|
|
|
int pb_id = g_key_file_get_integer(cfg_file, "PB", "id", &error);
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
double pb_refclock = g_key_file_get_double(cfg_file, "PB", "refclock", &error);
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
int pb_sync_bit = g_key_file_get_integer(cfg_file, "PB", "sync_line", &error);
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
int pb_sync = 0;
|
2017-02-06 21:31:58 +00:00
|
|
|
if (pb_sync_bit != 128) {
|
2014-06-26 11:10:51 +00:00
|
|
|
pb_sync = 1 << pb_sync_bit;
|
2017-02-06 21:31:58 +00:00
|
|
|
with_sync = 1;
|
|
|
|
my_pulseblaster = new SpinCorePulseBlaster24Bit(pb_id, pb_refclock, pb_sync);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
with_sync = 0;
|
|
|
|
my_pulseblaster = new SpinCorePulseBlaster24Bit(pb_id, pb_refclock,0);
|
|
|
|
}
|
2014-06-26 11:10:51 +00:00
|
|
|
/* configure PTS */
|
|
|
|
int pts_id = g_key_file_get_integer(cfg_file, "PTS", "id", &error);
|
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
my_pts = new PTS_latched(pts_id);
|
|
|
|
// PTS 500 has 0.36 or 0.72 above 200MHz ; PTS 310 has 0.225 degrees/step
|
2017-02-06 21:31:58 +00:00
|
|
|
my_pts->phase_step = (float) g_key_file_get_double(cfg_file, "PTS", "phase_stepsize", &error);
|
2014-06-26 11:10:51 +00:00
|
|
|
if (error)
|
2018-03-26 13:21:11 +00:00
|
|
|
g_error("%s",error->message);
|
2014-06-26 11:10:51 +00:00
|
|
|
error = NULL;
|
|
|
|
|
|
|
|
// publish devices
|
|
|
|
the_pg = my_pulseblaster;
|
|
|
|
the_adc = my_adc;
|
|
|
|
the_fg = my_pts;
|
|
|
|
}
|
|
|
|
|
|
|
|
result* experiment(const state& exp)
|
|
|
|
{
|
|
|
|
result* r = NULL;
|
|
|
|
for (size_t tries = 0; r == NULL && core::term_signal == 0 && tries < 102; ++tries)
|
|
|
|
{
|
|
|
|
state* work_copy = exp.copy_flat();
|
|
|
|
if (work_copy == NULL)
|
2017-02-06 21:31:58 +00:00
|
|
|
return new error_result(1, "could not create work copy of experiment sequence");
|
2014-06-26 11:10:51 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
if (the_fg != NULL)
|
|
|
|
the_fg->set_frequency(*work_copy);
|
|
|
|
if (the_adc != NULL)
|
|
|
|
the_adc->set_daq(*work_copy);
|
2017-02-06 21:31:58 +00:00
|
|
|
else
|
|
|
|
throw ADC_exception("the_adc == NULL\n");
|
2014-06-26 11:10:51 +00:00
|
|
|
// the pulse generator is necessary
|
2017-02-06 21:31:58 +00:00
|
|
|
if (with_sync) {
|
|
|
|
my_pulseblaster->run_pulse_program_w_sync(*work_copy, my_adc->get_sample_clock_frequency());
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
//experiment_prepare_dacs(work_copy);
|
|
|
|
//experiment_run_pulse_program(work_copy);
|
|
|
|
the_pg->run_pulse_program(*work_copy);
|
|
|
|
}
|
2014-06-26 11:10:51 +00:00
|
|
|
// wait for pulse generator
|
|
|
|
the_pg->wait_till_end();
|
2017-02-06 21:31:58 +00:00
|
|
|
|
2014-06-26 11:10:51 +00:00
|
|
|
// after that, the result must be available
|
2017-02-06 21:31:58 +00:00
|
|
|
if (the_adc != NULL) {
|
2014-06-26 11:10:51 +00:00
|
|
|
r = the_adc->get_samples();
|
2017-02-06 21:31:58 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-06-26 11:10:51 +00:00
|
|
|
r = new adc_result(1, 0, NULL);
|
2017-02-06 21:31:58 +00:00
|
|
|
throw ADC_exception("ADC result not available");
|
|
|
|
}
|
2014-06-26 11:10:51 +00:00
|
|
|
}
|
|
|
|
catch (const RecoverableException &e)
|
|
|
|
{
|
2014-08-01 14:18:05 +00:00
|
|
|
// there is a weird bug with string memory allocation for large messages, resize manually to cure this
|
|
|
|
std::string str;
|
|
|
|
str.resize(strlen(e.what()));
|
|
|
|
str = e.what();
|
|
|
|
r = new error_result(1, str);
|
2014-06-26 11:10:51 +00:00
|
|
|
}
|
|
|
|
delete work_copy;
|
|
|
|
if (core::quit_signal != 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~general_hardware()
|
|
|
|
{
|
|
|
|
if (the_adc != NULL)
|
|
|
|
delete the_adc;
|
|
|
|
if (the_fg != NULL)
|
|
|
|
delete the_fg;
|
|
|
|
if (the_pg != NULL)
|
|
|
|
delete the_pg;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief brings standard core together with the general NMR hardware
|
|
|
|
*/
|
|
|
|
class general_core: public core
|
|
|
|
{
|
|
|
|
std::string the_name;
|
|
|
|
public:
|
|
|
|
general_core(const core_config& conf) :
|
|
|
|
core(conf)
|
|
|
|
{
|
|
|
|
the_hardware = new general_hardware();
|
|
|
|
the_name = "berta core";
|
|
|
|
}
|
|
|
|
virtual const std::string& core_name() const
|
|
|
|
{
|
|
|
|
return the_name;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
@}
|
|
|
|
*/
|
|
|
|
|
|
|
|
int main(int argc, const char** argv)
|
|
|
|
{
|
|
|
|
int return_result = 0;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
core_config my_conf(argv, argc);
|
|
|
|
// setup input and output
|
|
|
|
general_core my_core(my_conf);
|
|
|
|
// start core application
|
|
|
|
my_core.run();
|
|
|
|
}
|
|
|
|
catch (const DamarisException& e)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s\n", e.what());
|
|
|
|
return_result = 1;
|
|
|
|
}
|
|
|
|
return return_result;
|
|
|
|
}
|