Files

1580 lines
50 KiB
C++

/*
* Osci.cpp
* DRS oscilloscope main class
* $Id: Osci.cpp 22326 2016-10-10 14:21:29Z ritt $
*/
#include "DRSOscInc.h"
#include "rb.h"
#ifndef _MSC_VER
#include <sys/time.h>
#endif
int g_rbh;
#define RB_SIZE (1024*1024*4) // 4 MB fits 60 waveforms for four boards
void GetTimeStamp(TIMESTAMP &ts);
/*------------------------------------------------------------------*/
Osci::Osci(double samplingSpeed, bool mthread)
{
m_drs = NULL;
m_thread = NULL;
m_running = false;
m_single = false;
m_armed = false;
m_samplingSpeed = samplingSpeed;
m_triggerCell[0] = 0;
m_writeSR[0] = 0;
m_waveDepth = 1024;
m_trgDelay = 0;
for (int i=0 ; i<4 ; i++)
m_trgLevel[i] = 0;
for (int b=0 ; b<MAX_N_BOARDS ; b++) {
m_trgNegative[b] = false;
m_trgMode[b] = TM_AUTO;
m_trgConfig[b] = 0;
m_refClk[b] = false;
for (int i=0 ; i<4 ; i++)
m_chnOn[b][i] = false;
}
m_clkOn = false;
m_calibOn = false;
m_evSerial = 1;
m_calibrated = true;
m_calibrated2 = true;
m_tcalon = true;
m_rotated = true;
m_nDRS = 0;
m_board = 0;
m_chip = 0;
m_chnOffset = 0;
m_chnSection = 0;
m_spikeRemoval = false;
m_inputRange = 0;
m_skipDisplay = false;
m_multiBoard = false;
m_debugMsg[0] = 0;
ScanBoards();
SelectBoard(0);
SelectChannel(0, 0);
if (mthread) {
rb_create(RB_SIZE, 4*MAX_N_BOARDS*(9*1024*2+64), &g_rbh);
m_thread = new OsciThread(this);
}
}
/*------------------------------------------------------------------*/
Osci::~Osci()
{
if (m_thread) {
m_thread->StopThread();
wxThread::Sleep(100);
}
if (g_rbh)
rb_delete(g_rbh);
delete m_drs;
}
/*------------------------------------------------------------------*/
void Osci::StopThread(void)
{
if (m_thread) {
do {
m_thread->StopThread();
wxThread::Sleep(100);
} while (!m_thread->IsFinished());
}
}
/*------------------------------------------------------------------*/
void GetTimeStamp(TIMESTAMP &ts)
{
#ifdef _MSC_VER
SYSTEMTIME t;
static unsigned int ofs = 0;
GetLocalTime(&t);
if (ofs == 0)
ofs = timeGetTime() - t.wMilliseconds;
ts.Year = t.wYear;
ts.Month = t.wMonth;
ts.Day = t.wDay;
ts.Hour = t.wHour;
ts.Minute = t.wMinute;
ts.Second = t.wSecond;
ts.Milliseconds = (timeGetTime() - ofs) % 1000;
#else
struct timeval t;
struct tm *lt;
time_t now;
gettimeofday(&t, NULL);
time(&now);
lt = localtime(&now);
ts.Year = lt->tm_year+1900;
ts.Month = lt->tm_mon+1;
ts.Day = lt->tm_mday;
ts.Hour = lt->tm_hour;
ts.Minute = lt->tm_min;
ts.Second = lt->tm_sec;
ts.Milliseconds = t.tv_usec/1000;
#endif /* OS_UNIX */
}
/*------------------------------------------------------------------*/
int Osci::ScanBoards()
{
/* pause readout thread if present */
if (m_thread)
m_thread->Enable(false);
if (m_drs)
delete m_drs;
m_drs = new DRS();
m_drs->SortBoards();
m_nDRS = m_drs->GetNumberOfBoards();
m_board = 0;
for (int i=0 ; i< m_nDRS ; i++) {
DRSBoard *b = m_drs->GetBoard(i);
b->Init();
if (b->GetBoardType() == 3) { // DRS2 board
b->SetDominoMode(1);
b->EnableTrigger(1, 0); // fast trigger
if (m_samplingSpeed > 2)
m_samplingSpeed = 2;
b->SetFrequency(m_samplingSpeed, true);
m_samplingSpeed = b->GetNominalFrequency();
b->SetNumberOfChannels(10);
#ifdef _MSC_VER
b->SetCalibrationDirectory("C:/experiment/calibrations");
#else
b->SetCalibrationDirectory("/home/meg/meg/online/calibrations");
#endif
if (i == 0)
printf("Reading calibration for sampling speed %lg GSPS\n", m_samplingSpeed);
if (!b->GetResponseCalibration()->ReadCalibration(0) ||
!b->GetResponseCalibration()->ReadCalibration(1)) {
wxString str;
str.Printf(wxT("Cannot read calibration for %1.1lf GSPS VME board slot %2d %s, serial #%d"),
m_samplingSpeed,
(b->GetSlotNumber() >> 1)+2,
((b->GetSlotNumber() & 1) == 0) ? "upper" : "lower",
b->GetBoardSerialNumber());
wxMessageBox(str, wxT("DRS Oscilloscope Error"), wxOK | wxICON_STOP, NULL);
} else {
printf("Calibration read for VME board slot %2d %s, serial #%04d\n",
(b->GetSlotNumber() >> 1)+2,
((b->GetSlotNumber() & 1) == 0) ? "upper" : "lower",
b->GetBoardSerialNumber());
}
} else { // DRS4 board
/* obtain default sampling speed from calibration of first board */
if (i == 0)
m_samplingSpeed = b->GetCalibratedFrequency();
b->SetFrequency(m_samplingSpeed, true);
m_samplingSpeed = b->GetNominalFrequency();
if (b->GetTransport() == TR_USB2)
b->SetChannelConfig(0, 8, 8);
else
b->SetChannelConfig(7, 8, 8);
b->SetDecimation(0);
b->SetDominoMode(1);
b->SetReadoutMode(1);
b->SetDominoActive(1);
if (b->GetBoardType() == 5 || b->GetBoardType() == 7 || b->GetBoardType() == 8 || b->GetBoardType() == 9) {
b->SetTranspMode(1); // Evaluation board with build-in trigger
b->EnableTrigger(0, 1); // Enable analog trigger
b->SetTriggerConfig(1<<0); // on CH0
} else if (b->GetBoardType() == 6) {
b->SetTranspMode(0); // VPC Mezzanine board
b->EnableTrigger(0, 0); // Disable analog trigger
}
b->SetRefclk(0);
b->SetFrequency(m_samplingSpeed, true);
b->EnableAcal(0, 0);
b->EnableTcal(0, 0);
b->StartDomino();
}
}
/* resume readout thread if present */
if (m_thread)
m_thread->Enable(true);
return m_nDRS;
}
/*------------------------------------------------------------------*/
void Osci::CheckTimingCalibration()
{
for (int i=0 ; i< m_nDRS ; i++) {
DRSBoard *b = m_drs->GetBoard(i);
if (b->GetDRSType() == 4) {
if (!b->IsVoltageCalibrationValid()) {
wxString str;
if (b->GetTransport() != TR_VME) {
str.Printf(wxT("Board on USB%d has invalid voltage calibration\nOnly raw data will be displayed"),
b->GetSlotNumber());
} else {
str.Printf(wxT("VME board in slot %2d %s has invalid voltage calibration\nOnly raw data will be displayed"),
(b->GetSlotNumber() >> 1)+2,
((b->GetSlotNumber() & 1) == 0) ? "upper" : "lower");
}
wxMessageBox(str, wxT("DRS Oscilloscope Warning"), wxOK | wxICON_EXCLAMATION);
} else {
if (!b->IsTimingCalibrationValid()) {
wxString str;
if (b->GetTransport() != TR_VME) {
if (b->GetCalibratedFrequency() == -1)
str.Printf(wxT("Board on USB%d has been timing calibrated with an old method. You must redo the timing calibration to obtain precise timing results."), b->GetSlotNumber());
else
str.Printf(wxT("Board on USB%d has been timing calibrated at %1.4lg GSPS. You must redo the timing calibration at %1.4lg GSPS to obtain precise timing results."),
b->GetSlotNumber(), b->GetCalibratedFrequency(), m_samplingSpeed);
} else {
str.Printf(wxT("VME board in slot %2d %s has been timing calibrated at %1.4lg GSPS. You must redo the timing calibration at %1.4lg GSPS to obtain precise timing results."),
(b->GetSlotNumber() >> 1)+2,
((b->GetSlotNumber() & 1) == 0) ? "upper" : "lower",
b->GetCalibratedFrequency(), b->GetNominalFrequency());
}
wxMessageBox(str, wxT("DRS Oscilloscope Warning"), wxOK | wxICON_EXCLAMATION);
}
}
}
}
}
/*------------------------------------------------------------------*/
void Osci::SelectBoard(int board)
{
if (board >= m_nDRS)
return;
/* pause readout thread if present */
if (m_thread)
m_thread->Enable(false);
m_board = board;
if (m_thread)
DrainEvents();
/* resume readout thread if present */
if (m_thread)
m_thread->Enable(true);
}
/*------------------------------------------------------------------*/
void Osci::SelectChannel(int firstChannel, int chnSection)
{
if (m_drs->GetNumberOfBoards() == 0)
return;
/* pause readout thread if present */
if (m_thread)
m_thread->Enable(false);
for (int bi=0 ; bi < m_nDRS ; bi++) {
DRSBoard *b = m_drs->GetBoard(bi);
/* stop drs_readout state machine to be ready for configuration change */
if (b->IsBusy()) {
b->SoftTrigger();
for (int i=0 ; i<10 && b->IsBusy() ; i++)
wxMilliSleep(10);
}
if (b->GetBoardType() == 6 && b->GetTransport() == TR_USB2) {
if (firstChannel == 0 || firstChannel == 2) {
if (chnSection == 0)
b->SetChannelConfig(1, 8, 8);
else
b->SetChannelConfig(0, 8, 8);
} else {
if (chnSection == 0)
b->SetChannelConfig(3, 8, 8);
else
b->SetChannelConfig(2, 8, 8);
}
m_chip = firstChannel;
m_chnOffset = 0;
m_chnSection = chnSection;
} else {
m_chip = firstChannel;
m_chnOffset = chnSection;
m_chnSection = chnSection;
if (b->GetBoardType() == 3) {
m_chip = firstChannel / 2;
m_chnOffset = (firstChannel % 2)* 4;
}
if (b->GetBoardType() == 5 || b->GetBoardType() == 7 || b->GetBoardType() == 8 || b->GetBoardType() == 9) {
if (chnSection == 2)
b->SetChannelConfig(0, 8, 4);
else
b->SetChannelConfig(0, 8, 8);
} else {
if (chnSection == 2)
b->SetChannelConfig(7, 8, 4);
else
b->SetChannelConfig(7, 8, 8);
}
}
/* resume readout thread if present */
if (m_thread)
m_thread->Enable(true);
}
}
/*------------------------------------------------------------------*/
void Osci::SetMultiBoard(bool flag)
{
m_multiBoard = flag;
}
/*------------------------------------------------------------------*/
OsciThread::OsciThread(Osci *o) : wxThread()
{
m_osci = o;
m_enabled = false;
m_finished = false;
m_active = false;
m_stopThread = false;
Create();
Run();
}
/*------------------------------------------------------------------*/
void OsciThread::Enable(bool flag)
{
m_enabled = flag;
if (!flag)
while (m_active)
wxThread::Sleep(10);
}
/*------------------------------------------------------------------*/
void OsciThread::StopThread()
{
m_stopThread = true;
while (!m_finished)
wxThread::Sleep(10);
}
/*------------------------------------------------------------------*/
bool OsciThread::IsIdle()
{
if (m_osci->IsRunning() && m_sw1.Time() > 1000)
return true;
if (m_osci->IsSingle() && m_osci->IsArmed() && m_sw1.Time() > 1000)
return true;
return false;
}
/*------------------------------------------------------------------*/
void OsciThread::ResetSW()
{
m_sw1.Start();
}
/*------------------------------------------------------------------*/
void *OsciThread::Entry()
{
int size, index, status, n, m;
unsigned char *pdata;
unsigned short *p;
bool autoTriggered;
DRSBoard *b;
TIMESTAMP ts;
bool skip_event;
time_t lastTempRead = 0;
n = m = 0;
autoTriggered = false;
do {
if (m_osci->GetDRS()->GetNumberOfBoards() > 0 && m_enabled) {
m_active = true;
if (m_osci->HasTriggered()) {
skip_event = false;
n = 0;
if (!autoTriggered)
m_sw1.Start(); // we got a real trigger
autoTriggered = false;
// wait for space in ring buffer
do {
status = rb_get_wp(g_rbh, (void **)&pdata, 100);
if (status == RB_SUCCESS) {
// put number of boards into ring buffer
p = (unsigned short *)pdata;
*p++ = m_osci->IsMultiBoard() ? m_osci->GetDRS()->GetNumberOfBoards() : 1;
for (index = 0 ; index < m_osci->GetDRS()->GetNumberOfBoards() ; index++) {
b = m_osci->GetBoard(index);
if (!m_osci->IsMultiBoard() && m_osci->GetCurrentBoard() != b)
continue;
// abort event readout if slave board has not triggered
if (m_osci->IsMultiBoard())
if (index > 0)
if (b->IsBusy())
skip_event = true;
if (skip_event)
break;
// transfer waveforms
b->TransferWaves((unsigned char *)p);
size = b->GetWaveformBufferSize();
p += size/sizeof(unsigned short);
*p++ = b->GetStopCell(0);
*p++ = b->GetStopWSR(0);
*p++ = (unsigned short)b->GetBoardSerialNumber();
}
// restart boards in inverted order (master last)
for (index = m_osci->GetDRS()->GetNumberOfBoards()-1 ; index>=0 ; index--) {
b = m_osci->GetBoard(index);
if (!m_osci->IsMultiBoard() && m_osci->GetCurrentBoard() != b)
continue;
if (m_osci->IsRunning()) {
// only if not in multi buffer mode
if (!b->IsMultiBuffer())
b->StartDomino();
}
}
// in single mode, just clear armed flag
if (m_osci->IsSingle())
m_osci->SetArmed(false);
if (skip_event)
break;
// add timestamp
GetTimeStamp(ts);
memcpy(p, &ts, sizeof(ts));
p += sizeof(ts)/sizeof(unsigned short);
size = (unsigned char*)p - pdata;
// commit data to ring buffer
rb_increment_wp(g_rbh, size);
} else
wxThread::Sleep(10);
if (m_stopThread)
break;
} while (status != RB_SUCCESS && !TestDestroy());
#ifndef _MSC_VER // Needed for Linux only, otherwise GUI freezes
if (m++ % 10 == 0)
wxThread::Yield();
#endif
} else {
if (m_osci->GetTriggerMode() == TM_AUTO && m_osci->IsRunning() &&
m_sw1.Time() > 1000) {
if (m_sw2.Time() > 300) {
wxThread::Sleep(30); // sleep 3 times a second a bit to test for real trigger
m_sw2.Start();
}
if (!m_osci->HasTriggered()) {
m_osci->SingleTrigger();
autoTriggered = true;
}
} else {
n++;
// sleep once in a while to save CPU
if (n == 100) {
n = 0;
wxThread::Sleep(100);
} else
wxThread::Yield();
}
}
} else {
wxThread::Sleep(10);
m_active = false;
}
// read temperature once every second
time_t now;
time(&now);
if (now > lastTempRead) {
lastTempRead = now;
for (int i=0 ; i<m_osci->GetDRS()->GetNumberOfBoards() ; i++) {
b = m_osci->GetBoard(i);
b->ReadTemperature();
}
}
} while (!m_stopThread);
m_finished = true;
return NULL;
}
/*------------------------------------------------------------------*/
void Osci::SetRunning(bool flag)
{
m_running = flag;
if (m_running)
Start();
else {
Stop();
if (m_drs->GetNumberOfBoards() > 0)
DrainEvents();
}
}
/*------------------------------------------------------------------*/
void Osci::Enable(bool flag)
{
if (m_thread)
m_thread->Enable(flag);
}
/*------------------------------------------------------------------*/
void Osci::Start()
{
/* start domino wave */
if (m_multiBoard) {
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
m_drs->GetBoard(i)->StartDomino();
} else if (m_drs->GetNumberOfBoards())
m_drs->GetBoard(m_board)->StartDomino();
m_armed = true;
}
/*------------------------------------------------------------------*/
void Osci::Stop()
{
/* stop domino wave */
if (m_multiBoard) {
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
m_drs->GetBoard(i)->SoftTrigger();
} else if (m_drs->GetNumberOfBoards())
m_drs->GetBoard(m_board)->SoftTrigger();
m_armed = false;
}
/*------------------------------------------------------------------*/
void Osci::DrainEvents()
{
while (HasNewEvent())
ReadWaveforms();
}
/*------------------------------------------------------------------*/
void Osci::SetSingle(bool flag)
{
m_single = flag;
if (m_thread)
m_thread->ResetSW();
}
/*------------------------------------------------------------------*/
void Osci::SingleTrigger()
{
if (m_multiBoard) {
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
m_drs->GetBoard(i)->SoftTrigger();
} else if (m_drs->GetNumberOfBoards())
m_drs->GetBoard(m_board)->SoftTrigger();
}
/*------------------------------------------------------------------*/
bool Osci::HasNewEvent()
{
int n;
if (m_drs->GetNumberOfBoards() > 0) {
if (m_thread) {
rb_get_buffer_level(g_rbh, &n);
return n > 0;
} else {
if (m_armed && m_drs->GetBoard(m_board)->IsBusy() == 0)
return true;
return false;
}
} else
return m_running;
}
/*------------------------------------------------------------------*/
bool Osci::HasTriggered()
{
int i;
if (m_drs->GetNumberOfBoards() > 0) {
if (m_multiBoard) // only first board can trigger
i = 0;
else
i = m_board;
if (m_running)
return m_drs->GetBoard(i)->IsEventAvailable() > 0;
if (m_single && m_armed)
return m_drs->GetBoard(i)->IsEventAvailable() > 0;
return false;
}
return true;
}
/*------------------------------------------------------------------*/
bool Osci::IsIdle()
{
if (m_thread)
return m_thread->IsIdle();
return false;
}
/*------------------------------------------------------------------*/
void Osci::ReadWaveforms()
{
unsigned char *pdata;
unsigned short *p;
int size = 0;
DRSBoard * b;
m_skipDisplay = false;
m_armed = false;
m_nBoards = 1;
if (m_drs->GetNumberOfBoards() == 0) {
for (int w=0 ; w<4 ; w++)
for (int i=0 ; i<GetWaveformDepth(w) ; i++) {
m_waveform[0][w][i] = sin(i/m_samplingSpeed/10*M_PI+w*M_PI/4)*100;
m_waveform[0][w][i] += ((double)rand()/RAND_MAX-0.5)*5;
m_time[0][w][i] = 1/m_samplingSpeed*i;
}
m_waveDepth = kNumberOfBins;
GetTimeStamp(m_evTimestamp);
} else {
int ofs = m_chnOffset;
int chip = m_chip;
if (m_drs->GetBoard(m_board)->GetBoardType() == 3) {
// DRS2 Mezzanine Board 1
m_drs->GetBoard(m_board)->TransferWaves();
int tc = m_drs->GetBoard(m_board)->GetTriggerCell(chip);
m_drs->GetBoard(m_board)->GetTime(chip, 0, m_samplingSpeed, tc, m_time[0][0], m_tcalon, m_rotated);
m_drs->GetBoard(m_board)->GetWave(chip, 0+ofs, m_waveform[0][0], m_calibrated, tc, !m_rotated);
m_drs->GetBoard(m_board)->GetWave(chip, 1+ofs, m_waveform[0][1], m_calibrated, tc, !m_rotated);
m_drs->GetBoard(m_board)->GetWave(chip, 2+ofs, m_waveform[0][2], m_calibrated, tc, !m_rotated);
if (m_clkOn)
m_drs->GetBoard(m_board)->GetWave(chip, 9, m_waveform[0][3], m_calibrated, tc, !m_rotated);
else
m_drs->GetBoard(m_board)->GetWave(chip, 3+ofs, m_waveform[0][3], m_calibrated, tc, !m_rotated);
} else if (m_drs->GetBoard(m_board)->GetBoardType() == 5 || m_drs->GetBoard(m_board)->GetBoardType() == 7 ||
m_drs->GetBoard(m_board)->GetBoardType() == 8 || m_drs->GetBoard(m_board)->GetBoardType() == 9) {
// DRS4 Evaluation Boards 1.1 + 3.0 + 4.0
if (m_thread) {
// get waveforms from ring buffer
if (rb_get_rp(g_rbh, (void **)&pdata, 0) != RB_SUCCESS)
return;
// transfer waveforms to buffer
size = m_drs->GetBoard(m_board)->GetWaveformBufferSize();
p = (unsigned short *)pdata;
m_nBoards = *p++; // number of boards
for (int i=0 ; i<m_nBoards ; i++) {
memcpy(m_wavebuffer[i], p, size);
p += size/sizeof(unsigned short);
m_triggerCell[i] = *p++;
m_writeSR[i] = *p++;
m_boardSerial[i] = *p++;
}
// transfer timestamp
memcpy(&m_evTimestamp, p, sizeof(m_evTimestamp));
p += sizeof(TIMESTAMP)/sizeof(unsigned short);
size = (unsigned char*)p - pdata;
// free space in ring buffer
rb_increment_rp(g_rbh, size);
} else {
// get waveforms directly from device
m_drs->GetBoard(m_board)->TransferWaves(m_wavebuffer[0], 0, 8);
m_triggerCell[0] = m_drs->GetBoard(m_board)->GetStopCell(chip);
m_writeSR[0] = m_drs->GetBoard(m_board)->GetStopWSR(chip);
GetTimeStamp(m_evTimestamp);
}
for (int i=0 ; i<m_nBoards ; i++) {
if (m_nBoards > 1)
b = m_drs->GetBoard(i);
else
b = m_drs->GetBoard(m_board);
// obtain time arrays
m_waveDepth = b->GetChannelDepth();
for (int w=0 ; w<4 ; w++)
b->GetTime(0, w*2, m_triggerCell[i], m_time[i][w], m_tcalon, m_rotated);
if (m_clkOn && GetWaveformDepth(0) > kNumberOfBins) {
for (int j=0 ; j<kNumberOfBins ; j++)
m_timeClk[i][j] = m_time[i][0][j] + GetWaveformLength()/2;
} else {
for (int j=0 ; j<kNumberOfBins ; j++)
m_timeClk[i][j] = m_time[i][0][j];
}
// decode and calibrate waveforms from buffer
if (b->GetChannelCascading() == 2) {
b->GetWave(m_wavebuffer[i], 0, 0, m_waveform[i][0], m_calibrated, m_triggerCell[i], m_writeSR[i], !m_rotated, 0, m_calibrated2);
b->GetWave(m_wavebuffer[i], 0, 1, m_waveform[i][1], m_calibrated, m_triggerCell[i], m_writeSR[i], !m_rotated, 0, m_calibrated2);
b->GetWave(m_wavebuffer[i], 0, 2, m_waveform[i][2], m_calibrated, m_triggerCell[i], m_writeSR[i], !m_rotated, 0, m_calibrated2);
if (m_clkOn && b->GetBoardType() < 9)
b->GetWave(m_wavebuffer[i], 0, 8, m_waveform[i][3], m_calibrated, m_triggerCell[i], 0, !m_rotated);
else
b->GetWave(m_wavebuffer[i], 0, 3, m_waveform[i][3], m_calibrated, m_triggerCell[i], m_writeSR[i], !m_rotated, 0, m_calibrated2);
if (m_spikeRemoval)
RemoveSpikes(i, true);
} else {
b->GetWave(m_wavebuffer[i], 0, 0+ofs, m_waveform[i][0], m_calibrated, m_triggerCell[i], 0, !m_rotated, 0, m_calibrated2);
b->GetWave(m_wavebuffer[i], 0, 2+ofs, m_waveform[i][1], m_calibrated, m_triggerCell[i], 0, !m_rotated, 0, m_calibrated2);
b->GetWave(m_wavebuffer[i], 0, 4+ofs, m_waveform[i][2], m_calibrated, m_triggerCell[i], 0, !m_rotated, 0, m_calibrated2);
if (m_clkOn && b->GetBoardType() < 9)
b->GetWave(m_wavebuffer[i], 0, 8, m_waveform[i][3], m_calibrated, m_triggerCell[i], 0, !m_rotated);
else
b->GetWave(m_wavebuffer[i], 0, 6+ofs, m_waveform[i][3], m_calibrated, m_triggerCell[i], 0, !m_rotated, 0, m_calibrated2);
if (m_spikeRemoval)
RemoveSpikes(i, false);
}
// extrapolate the first two samples (are noisy)
for (int j=0 ; j<4 ; j++) {
m_waveform[i][j][1] = 2*m_waveform[i][j][2] - m_waveform[i][j][3];
m_waveform[i][j][0] = 2*m_waveform[i][j][1] - m_waveform[i][j][2];
}
}
} else if (m_drs->GetBoard(m_board)->GetBoardType() == 6) {
// DRS4 Mezzanine Board 1
m_drs->GetBoard(m_board)->TransferWaves(0, 8);
m_triggerCell[0] = m_drs->GetBoard(m_board)->GetStopCell(chip);
m_writeSR[0] = m_drs->GetBoard(m_board)->GetStopWSR(chip);
m_waveDepth = m_drs->GetBoard(m_board)->GetChannelDepth();
m_drs->GetBoard(m_board)->GetTime(chip, 0, m_samplingSpeed, m_triggerCell[0], m_time[0][0], m_tcalon, m_rotated);
if (m_clkOn && GetWaveformDepth(0) > kNumberOfBins) {
for (int i=0 ; i<kNumberOfBins ; i++)
m_timeClk[0][i] = m_time[0][0][i] + GetWaveformLength()/2;
} else {
for (int i=0 ; i<kNumberOfBins ; i++)
m_timeClk[0][i] = m_time[0][0][i];
}
if (m_drs->GetBoard(m_board)->GetChannelCascading() == 2) {
m_drs->GetBoard(m_board)->GetWave(chip, 0, m_waveform[0][0], m_calibrated, m_triggerCell[0], m_writeSR[0], !m_rotated, 0, m_calibrated2);
m_drs->GetBoard(m_board)->GetWave(chip, 1, m_waveform[0][1], m_calibrated, m_triggerCell[0], m_writeSR[0], !m_rotated, 0, m_calibrated2);
m_drs->GetBoard(m_board)->GetWave(chip, 2, m_waveform[0][2], m_calibrated, m_triggerCell[0], m_writeSR[0], !m_rotated, 0, m_calibrated2);
if (m_clkOn)
m_drs->GetBoard(m_board)->GetWave(chip, 8, m_waveform[0][3], m_calibrated, m_triggerCell[0], m_writeSR[0], !m_rotated);
else {
m_drs->GetBoard(m_board)->GetWave(chip, 3, m_waveform[0][3], m_calibrated, m_triggerCell[0], m_writeSR[0], !m_rotated, 0, m_calibrated2);
if (m_spikeRemoval)
RemoveSpikes(0, true);
}
} else {
m_drs->GetBoard(m_board)->GetWave(chip, 0+ofs, m_waveform[0][0], m_calibrated, m_triggerCell[0], 0, !m_rotated, 0, m_calibrated2);
m_drs->GetBoard(m_board)->GetWave(chip, 2+ofs, m_waveform[0][1], m_calibrated, m_triggerCell[0], 0, !m_rotated, 0, m_calibrated2);
m_drs->GetBoard(m_board)->GetWave(chip, 4+ofs, m_waveform[0][2], m_calibrated, m_triggerCell[0], 0, !m_rotated, 0, m_calibrated2);
if (m_clkOn)
m_drs->GetBoard(m_board)->GetWave(chip, 8, m_waveform[0][3], m_calibrated, m_triggerCell[0], 0, !m_rotated);
else {
m_drs->GetBoard(m_board)->GetWave(chip, 6+ofs, m_waveform[0][3], m_calibrated, m_triggerCell[0], 0, !m_rotated, 0, m_calibrated2);
if (m_spikeRemoval)
RemoveSpikes(0, false);
}
}
/* extrapolate the first two samples (are noisy) */
for (int i=0 ; i<4 ; i++) {
m_waveform[0][i][1] = 2*m_waveform[0][i][2] - m_waveform[0][i][3];
m_waveform[0][i][0] = 2*m_waveform[0][i][1] - m_waveform[0][i][2];
}
}
}
/* auto-restart in running mode */
if (m_thread == NULL && m_running)
Start();
}
/*------------------------------------------------------------------*/
unsigned char buffer[100000];
int Osci::SaveWaveforms(MXML_WRITER *xml, int fd)
{
char str[80], name[80];
unsigned char *p;
unsigned short d;
float t;
int size;
static unsigned char *buffer;
static int buffer_size = 0;
if (xml == NULL && fd == 0)
return 0;
if (xml) {
mxml_start_element(xml, "Event");
sprintf(str, "%d", m_evSerial);
mxml_write_element(xml, "Serial", str);
sprintf(str, "%4d/%02d/%02d %02d:%02d:%02d.%03d", m_evTimestamp.Year, m_evTimestamp.Month,
m_evTimestamp.Day, m_evTimestamp.Hour, m_evTimestamp.Minute, m_evTimestamp.Second,
m_evTimestamp.Milliseconds);
mxml_write_element(xml, "Time", str);
mxml_write_element(xml, "HUnit", "ns");
mxml_write_element(xml, "VUnit", "mV");
for (int b=0 ; b<m_nBoards ; b++) {
sprintf(str, "Board_%d", m_drs->GetBoard(b)->GetBoardSerialNumber());
mxml_start_element(xml, str);
sprintf(str, "%d", m_triggerCell[b]);
mxml_write_element(xml, "Trigger_Cell", str);
for (int i=0 ; i<4 ; i++) {
if (m_chnOn[b][i]) {
sprintf(str, "%u", m_drs->GetBoard(b)->GetScaler(i));
sprintf(name, "Scaler%d", i);
mxml_write_element(xml, name, str);
sprintf(str, "CHN%d", i+1);
mxml_start_element(xml, str);
strcpy(str, "\n");
for (int j=0 ; j<m_waveDepth ; j++) {
sprintf(str, "%1.3f,%1.1f", m_time[b][i][j], m_waveform[b][i][j]);
mxml_write_element(xml, "Data", str);
}
mxml_end_element(xml); // CHNx
}
}
mxml_end_element(xml); //Board
}
mxml_end_element(xml); // Event
}
if (fd) {
if (buffer_size == 0) {
buffer_size = 8; // file header + time header
buffer_size += m_nBoards * (4 + 4*(4+m_waveDepth*4)); // bin widths
buffer_size += 24 + m_nBoards * (8 + 4*(8+m_waveDepth*2));
buffer = (unsigned char *)malloc(buffer_size);
}
p = buffer;
if (m_evSerial == 1) {
memcpy(p, "DRS2", 4); // File identifier and version
p += 4;
// time calibration header
memcpy(p, "TIME", 4);
p += 4;
for (int b=0 ; b<m_nBoards ; b++) {
// store board serial number
sprintf((char *)p, "B#");
p += 2;
*(unsigned short *)p = m_drs->GetBoard(b)->GetBoardSerialNumber();
p += sizeof(unsigned short);
for (int i=0 ; i<4 ; i++) {
if (m_chnOn[b][i]) {
sprintf((char *)p, "C%03d", i+1);
p += 4;
float tcal[2048];
m_drs->GetBoard(b)->GetTimeCalibration(0, i*2, 0, tcal, 0);
for (int j=0 ; j<m_waveDepth ; j++) {
// save binary time as 32-bit float value
if (m_waveDepth == 2048) {
t = (tcal[j % 1024]+tcal[(j+1) % 1024])/2;
j++;
} else
t = tcal[j];
*(float *)p = t;
p += sizeof(float);
}
}
}
}
}
memcpy(p, "EHDR", 4);
p += 4;
*(int *)p = m_evSerial;
p += sizeof(int);
*(unsigned short *)p = m_evTimestamp.Year;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Month;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Day;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Hour;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Minute;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Second;
p += sizeof(unsigned short);
*(unsigned short *)p = m_evTimestamp.Milliseconds;
p += sizeof(unsigned short);
*(unsigned short *)p = (unsigned short)(m_inputRange * 1000); // range
p += sizeof(unsigned short);
for (int b=0 ; b<m_nBoards ; b++) {
// store board serial number
sprintf((char *)p, "B#");
p += 2;
*(unsigned short *)p = m_drs->GetBoard(b)->GetBoardSerialNumber();
p += sizeof(unsigned short);
// store trigger cell
sprintf((char *)p, "T#");
p += 2;
*(unsigned short *)p = m_triggerCell[b];
p += sizeof(unsigned short);
for (int i=0 ; i<4 ; i++) {
if (m_chnOn[b][i]) {
sprintf((char *)p, "C%03d", i+1);
p += 4;
unsigned int s = m_drs->GetBoard(b)->GetScaler(i);
memcpy(p, &s, sizeof(int));
p += sizeof(int);
for (int j=0 ; j<m_waveDepth ; j++) {
// save binary date as 16-bit value:
// 0 = -0.5V, 65535 = +0.5V for range 0
// 0 = -0.05V, 65535 = +0.95V for range 0.45
if (m_waveDepth == 2048) {
// in cascaded mode, save 1024 values as averages of the 2048 values
d = (unsigned short)(((m_waveform[b][i][j]+m_waveform[b][i][j+1])/2000.0 - m_inputRange + 0.5) * 65535);
*(unsigned short *)p = d;
p += sizeof(unsigned short);
j++;
} else {
d = (unsigned short)((m_waveform[b][i][j]/1000.0 - m_inputRange + 0.5) * 65535);
*(unsigned short *)p = d;
p += sizeof(unsigned short);
}
}
}
}
}
size = p-buffer;
assert(size <= buffer_size);
int n = write(fd, buffer, size);
if (n != size)
return -1;
}
m_evSerial++;
return 1;
}
/*------------------------------------------------------------------*/
void Osci::SetSamplingSpeed(double freq)
{
if (m_drs->GetNumberOfBoards() == 0) {
m_samplingSpeed = freq;
return;
}
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++) {
DRSBoard *b = m_drs->GetBoard(i);
b->SetFrequency(freq, true);
m_samplingSpeed = b->GetNominalFrequency();
wxMilliSleep(10);
if (b->GetDRSType() == 4 && !b->IsPLLLocked()) {
wxString str;
#ifdef HAVE_VME
if (b->GetTransport() == 1)
str.Printf(wxT("PLLs did not lock on VME board slot %2d %s, serial #%d"),
(b->GetSlotNumber() >> 1)+2, ((b->GetSlotNumber() & 1) == 0) ? "upper" : "lower",
b->GetBoardSerialNumber());
else
#endif
str.Printf(wxT("PLLs did not lock on USB board #%d, serial #%d"),
b->GetSlotNumber(), b->GetBoardSerialNumber());
wxMessageBox(str, wxT("DRS Oscilloscope Error"), wxOK | wxICON_STOP, NULL);
}
}
}
/*------------------------------------------------------------------*/
double Osci::GetSamplingSpeed()
{
if (m_drs->GetNumberOfBoards() > 0) {
DRSBoard *b = m_drs->GetBoard(m_board);
return b->GetNominalFrequency();
}
return m_samplingSpeed;
}
/*------------------------------------------------------------------*/
double Osci::GetTrueSamplingSpeed()
{
if (m_drs->GetNumberOfBoards() > 0)
return m_drs->GetBoard(m_board)->GetTrueFrequency();
return m_samplingSpeed;
}
/*------------------------------------------------------------------*/
double Osci::GetMaxSamplingSpeed()
{
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
if (m_drs->GetBoard(i)->GetDRSType() == 2)
return 4;
return 5;
}
/*------------------------------------------------------------------*/
double Osci::GetMinSamplingSpeed()
{
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
if (m_drs->GetBoard(i)->GetDRSType() == 4) {
if (m_drs->GetBoard(i)->GetBoardSerialNumber() == 2146 ||
m_drs->GetBoard(i)->GetBoardSerialNumber() == 2205 ||
m_drs->GetBoard(i)->GetBoardSerialNumber() == 2208)
return 0.5; // special modified boards for RFBeta & Slow Muons
return 0.7;
}
return 0.5;
}
/*------------------------------------------------------------------*/
int Osci::GetWaveformDepth(int channel)
{
if (channel == 4 && m_clkOn && m_waveDepth > kNumberOfBins)
return m_waveDepth - kNumberOfBins; // clock channel has only 1024 bins
return m_waveDepth;
}
/*------------------------------------------------------------------*/
float *Osci::GetTime(int b, int channel)
{
if (m_drs->GetNumberOfBoards() > 0) {
if (m_drs->GetBoard(m_board)->GetBoardType() < 9) {
if (channel == 3 && m_clkOn)
return (float *)m_timeClk[b];
}
}
return (float *)m_time[b][channel];
}
/*------------------------------------------------------------------*/
bool Osci::IsTCalibrated(void)
{
return m_drs->GetBoard(m_board)->IsTimingCalibrationValid();
}
/*------------------------------------------------------------------*/
bool Osci::IsVCalibrated(void)
{
return m_drs->GetBoard(m_board)->IsVoltageCalibrationValid();
}
/*------------------------------------------------------------------*/
bool Osci::GetTimeCalibration(int chip, int channel, int mode, float *time, bool force)
{
if (!force && !IsTCalibrated())
return false;
m_drs->GetBoard(m_board)->GetTimeCalibration(chip, channel, mode, time, force);
return true;
}
/*------------------------------------------------------------------*/
void Osci::SetTriggerLevel(double level)
{
m_trgLevel[0] = m_trgLevel[1] = m_trgLevel[2] = m_trgLevel[3] = level;
// only change trigger of current board
if (m_drs->GetNumberOfBoards() > 0)
m_drs->GetBoard(m_board)->SetTriggerLevel(level);
}
/*------------------------------------------------------------------*/
void Osci::SetTriggerPolarity(bool negative)
{
m_trgNegative[m_board] = negative;
if (m_drs->GetNumberOfBoards() > 0)
m_drs->GetBoard(m_board)->SetTriggerPolarity(negative);
}
/*------------------------------------------------------------------*/
void Osci::SetIndividualTriggerLevel(int channel, double level)
{
m_trgLevel[channel] = level;
if (m_drs->GetNumberOfBoards() > 0)
m_drs->GetBoard(m_board)->SetIndividualTriggerLevel(channel, level);
}
/*------------------------------------------------------------------*/
void Osci::SetTriggerDelay(int delay)
{
m_trgDelay = delay;
if (m_drs->GetNumberOfBoards() > 0)
m_drs->GetBoard(m_board)->SetTriggerDelayPercent(delay);
}
/*------------------------------------------------------------------*/
int Osci::GetTriggerDelay()
{
if (m_drs->GetNumberOfBoards() > 0)
return m_drs->GetBoard(m_board)->GetTriggerDelay();
else
return 0;
}
/*------------------------------------------------------------------*/
double Osci::GetTriggerDelayNs()
{
if (m_drs->GetNumberOfBoards() > 0)
return m_drs->GetBoard(m_board)->GetTriggerDelayNs();
else
return 0;
}
/*------------------------------------------------------------------*/
void Osci::SetTriggerConfig(int tc)
{
if (m_drs->GetBoard(m_board)->GetBoardType() == 8 || m_drs->GetBoard(m_board)->GetBoardType() == 9) {
m_drs->GetBoard(m_board)->EnableTrigger(1, 0); // enable trigger
m_drs->GetBoard(m_board)->SetTriggerConfig(tc);
}
}
/*------------------------------------------------------------------*/
void Osci::SetChnOn(int board, int chn, bool flag)
{
m_chnOn[board][chn] = flag;
}
/*------------------------------------------------------------------*/
void Osci::SetClkOn(bool flag)
{
m_clkOn = flag;
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++) {
m_drs->GetBoard(i)->EnableTcal(flag ? 1 : 0, 0);
if (m_drs->GetBoard(i)->GetBoardType() == 5 || m_drs->GetBoard(i)->GetBoardType() == 7 ||
m_drs->GetBoard(i)->GetBoardType() == 8 || m_drs->GetBoard(i)->GetBoardType() == 9)
m_drs->GetBoard(i)->SelectClockSource(0); // select sync. clock
}
}
/*------------------------------------------------------------------*/
void Osci::SetRefclk(int board, bool flag)
{
m_refClk[board] = flag;
// only change clock of current board and not all boards to allow daisy-chaining
if (m_drs->GetNumberOfBoards()) {
if (m_drs->GetBoard(board)->GetBoardType() == 6 ||
m_drs->GetBoard(board)->GetBoardType() == 8 ||
m_drs->GetBoard(board)->GetBoardType() == 9)
m_drs->GetBoard(board)->SetRefclk(flag);
}
}
/*------------------------------------------------------------------*/
void Osci::SetCalibVoltage(bool flag, double voltage)
{
m_calibOn = flag;
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
m_drs->GetBoard(i)->EnableAcal(flag, voltage);
}
/*------------------------------------------------------------------*/
void Osci::SetInputRange(double center)
{
m_inputRange = center;
for (int i=0 ; i< m_drs->GetNumberOfBoards() ; i++)
m_drs->GetBoard(i)->SetInputRange(center);
}
/*------------------------------------------------------------------*/
double Osci::GetCalibratedInputRange()
{
return m_drs->GetBoard(m_board)->GetCalibratedInputRange();
}
/*------------------------------------------------------------------*/
unsigned int Osci::GetScaler(int channel)
{
if (m_drs->GetNumberOfBoards() > 0)
return m_drs->GetBoard(m_board)->GetScaler(channel);
return 0;
}
/*------------------------------------------------------------------*/
void Osci::CorrectTriggerPoint(double t)
{
int i, n, min_i, trgChn;
double min_dt, dt, t0, t1, trigPoint[2*kNumberOfBins];
float *pt;
/*---- shift first channel according to trigger point ----*/
for (i=trgChn=0 ; i<5 ; i++)
if (m_trgConfig[0] & (1<<i)) {
trgChn = i;
break;
}
if (trgChn == 3 && m_clkOn)
pt = m_timeClk[0];
else
pt = m_time[0][trgChn];
if (trgChn < 4) {
// search and store all points
for (i = n = 0 ; i<m_waveDepth-1 ; i++) {
if (m_trgNegative[m_board]) {
if (m_waveform[0][trgChn][i] >= m_trgLevel[trgChn]*1000 &&
m_waveform[0][trgChn][i+1] < m_trgLevel[trgChn]*1000) {
dt = pt[i+1] - pt[i];
dt = dt * 1 / (1 + (m_trgLevel[trgChn]*1000-m_waveform[0][trgChn][i+1])/(m_waveform[0][trgChn][i]-m_trgLevel[trgChn]*1000));
trigPoint[n++] = pt[i] + dt;
}
} else {
if (m_waveform[0][trgChn][i] <= m_trgLevel[trgChn]*1000 &&
m_waveform[0][trgChn][i+1] > m_trgLevel[trgChn]*1000) {
dt = pt[i+1] - pt[i];
dt = dt * 1 / (1 + (m_waveform[0][trgChn][i+1]-m_trgLevel[trgChn]*1000)/(m_trgLevel[trgChn]*1000-m_waveform[0][trgChn][i]));
trigPoint[n++] = pt[i] + dt;
}
}
}
if (n > 2) {
/* search trigger point closest to trigger in case of many trigger points (like a sine wave) */
min_dt = 1e6;
min_i = -1;
for (i=0 ; i<n ; i++) {
if (fabs(trigPoint[i] - t) < min_dt) {
min_dt = fabs(trigPoint[i] - t);
min_i = i;
}
}
} else {
/* choose first trigger point, to avoid jumping of the trigger position after correction */
min_i = 0;
}
/* correct times to shift waveform to trigger point */
if (n > 0) {
dt = trigPoint[min_i] - t;
for (int b=0 ; b<m_nBoards ; b++) {
for (int w=0 ; w<4 ; w++)
for (i=0 ; i<m_waveDepth ; i++)
m_time[b][w][i] -= dt;
for (i=0 ; i<kNumberOfBins ; i++)
m_timeClk[b][i] -= dt;
}
}
}
/*---- shift other channels according to cell #0 in multi-board mode ----*/
t0 = m_time[0][0][(1024-m_triggerCell[0]) % 1024];
for (int b=1 ; b<m_nBoards ; b++) {
if (!m_refClk[b])
continue; // skip boards if no external refclk
t1 = m_time[b][0][(1024-m_triggerCell[b]) % 1024];
dt = t1 - t0;
if (dt < 0)
dt += 1024/m_drs->GetBoard(0)->GetTrueFrequency();
for (int w=0 ; w<4 ; w++)
for (i=0 ; i<m_waveDepth ; i++)
m_time[b][w][i] -= dt;
for (i=0 ; i<kNumberOfBins ; i++)
m_timeClk[b][i] -= dt;
}
}
/*------------------------------------------------------------------*/
int spos[1024];
void Osci::RemoveSpikes(int b, bool cascading)
{
int i, j, k, l, c, ofs, nChn;
double x, y;
int sp[8][10];
int rsp[10], rot_sp[10];
int n_sp[8], n_rsp;
float wf[8][2048];
int nNeighbor, nSymmetric;
nChn = cascading ? 8 : 4;
/* rotate waveform back relative to cell #0 */
if (cascading) {
if (m_rotated) {
for (i=0 ; i<nChn ; i++)
for (j=0 ; j<1024 ; j++)
wf[i][(j+m_triggerCell[b]) % 1024] = m_waveform[b][i/2][(i%2)*1024+j];
} else {
for (i=0 ; i<nChn ; i++)
for (j=0 ; j<1024 ; j++)
wf[i][j] = m_waveform[b][i/2][(i%2)*1024+j];
}
} else {
if (m_rotated) {
for (i=0 ; i<nChn ; i++)
for (j=0 ; j<1024 ; j++)
wf[i][(j+m_triggerCell[b]) % 1024] = m_waveform[b][i][j];
} else {
for (i=0 ; i<nChn ; i++)
for (j=0 ; j<1024 ; j++)
wf[i][j] = m_waveform[b][i][j];
}
}
memset(sp, 0, sizeof(sp));
memset(n_sp, 0, sizeof(n_sp));
memset(rsp, 0, sizeof(rsp));
n_rsp = 0;
/* find spikes with special high-pass filter */
for (j=0 ; j<1024 ; j++) {
for (i=0 ; i<nChn ; i++) {
if (-wf[i][j]+wf[i][(j+1) % 1024]+wf[i][(j+2) % 1024]-wf[i][(j+3) % 1024] > 20) {
if (n_sp[i] < 10) // record maximum of 10 spikes
sp[i][n_sp[i]++] = j;
else
return; // too many spikes -> something wrong
spos[j]++;
}
if (-wf[i][j]+wf[i][(j+1) % 1024]+wf[i][(j+2) % 1024]-wf[i][(j+3) % 1024] < -20) {
if (n_sp[i] < 10) // record maximum of 10 spikes
sp[i][n_sp[i]++] = j;
else
return; // too many spikes -> something wrong
spos[j]++;
}
}
}
/* find spikes at cell #0 and #1023 */
for (i=0 ; i<nChn ; i++) {
if (wf[i][0]+wf[i][1]-2*wf[i][2] > 20) {
if (n_sp[i] < 10)
sp[i][n_sp[i]++] = 0;
}
if (-2*wf[i][1021]+wf[i][1022]+wf[i][1023] > 20) {
if (n_sp[i] < 10)
sp[i][n_sp[i]++] = 1020;
}
}
/* go through all spikes and look for symmetric spikes and neighbors */
for (i=0 ; i<nChn ; i++) {
for (j=0 ; j<n_sp[i] ; j++) {
/* check if this spike has a symmetric partner in any channel */
for (k=nSymmetric=0 ; k<nChn ; k++) {
for (l=0 ; l<n_sp[k] ; l++)
if (sp[i][j] == (1020-sp[k][l]+1024) % 1024) {
nSymmetric++;
break;
}
}
/* check if this spike has same spike in any other channels */
for (k=nNeighbor=0 ; k<nChn ; k++)
if (i != k) {
for (l=0 ; l<n_sp[k] ; l++)
if (sp[i][j] == sp[k][l]) {
nNeighbor++;
break;
}
}
if (nSymmetric + nNeighbor >= 2) {
/* if at least two matching spikes, treat this as a real spike */
for (k=0 ; k<n_rsp ; k++)
if (rsp[k] == sp[i][j])
break;
if (n_rsp < 10 && k == n_rsp)
rsp[n_rsp++] = sp[i][j];
}
}
}
/* rotate spikes according to trigger cell */
if (m_rotated) {
for (i=0 ; i<n_rsp ; i++)
rot_sp[i] = (rsp[i] - m_triggerCell[b] + 1024) % 1024;
} else {
for (i=0 ; i<n_rsp ; i++)
rot_sp[i] = rsp[i];
}
/* recognize spikes if at least one channel has it */
for (k=0 ; k<n_rsp ; k++) {
for (i=0 ; i<nChn ; i++) {
if (cascading) {
c = i/2;
ofs = (i%2)*1024;
} else {
c = i;
ofs = 0;
}
if (k < n_rsp-1 && rsp[k] == 0 && rsp[k+1] == 1020) {
/* remove double spike */
j = rot_sp[k] > rot_sp[k+1] ? rot_sp[k+1] : rot_sp[k];
x = m_waveform[b][c][ofs+(j+1) % 1024];
y = m_waveform[b][c][ofs+(j+6) % 1024];
if (fabs(x-y) < 15) {
m_waveform[b][c][ofs+(j+2) % 1024] = x + 1*(y-x)/5;
m_waveform[b][c][ofs+(j+3) % 1024] = x + 2*(y-x)/5;
m_waveform[b][c][ofs+(j+4) % 1024] = x + 3*(y-x)/5;
m_waveform[b][c][ofs+(j+5) % 1024] = x + 4*(y-x)/5;
} else {
m_waveform[b][c][ofs+(j+2) % 1024] -= 14.8f;
m_waveform[b][c][ofs+(j+3) % 1024] -= 14.8f;
m_waveform[b][c][ofs+(j+4) % 1024] -= 14.8f;
m_waveform[b][c][ofs+(j+5) % 1024] -= 14.8f;
}
} else {
/* remove single spike */
x = m_waveform[b][c][ofs+rot_sp[k]];
y = m_waveform[b][c][ofs+(rot_sp[k]+3) % 1024];
if (fabs(x-y) < 15) {
m_waveform[b][c][ofs+(rot_sp[k]+1) % 1024] = x + 1*(y-x)/3;
m_waveform[b][c][ofs+(rot_sp[k]+2) % 1024] = x + 2*(y-x)/3;
} else {
m_waveform[b][c][ofs+(rot_sp[k]+1) % 1024] -= 14.8f;
m_waveform[b][c][ofs+(rot_sp[k]+2) % 1024] -= 14.8f;
}
}
}
if (k < n_rsp-1 && rsp[k] == 0 && rsp[k+1] == 1020)
k++; // skip second half of double spike
}
/* uncomment to show unfixed spikes
m_skipDisplay = true;
for (i=0 ; i<1024 ; i++)
for (j=0 ; j<nChn ; j++) {
if (m_waveform[j][i] > 10 || m_waveform[j][i] < -10) {
m_skipDisplay = false;
break;
}
}
*/
}
/*------------------------------------------------------------------*/