Skip to content
Snippets Groups Projects
worker.cpp 18.90 KiB
#include "worker.h"
#include "TSystem.h"
#include <QFile>
#include <QString>
#include <QTextStream>
#include <QDataStream>
#include <QDebug>
#include <QDir>

bool Worker::processCoincidence(const std::vector<MyPulse>& pulses, Cone& newCone){
    // fill energy spectrum
    // TODO
    if (pulses.size() != 2)
    {
        return false;
    }
    // energy cut
    double Etot = pulses[1].height + pulses[0].height ;
    if (config->energyCut)
    {
        if (Etot < config->energyLow || Etot > config->energyUp)
        {
            return false;
        }
    }
//    if (pulses[0].height < 0.01 || pulses[1].height < 0.01)
//        return false;
    double cosHalfangle = 1 - pulses[0].height * 511 / (pulses[1].height * Etot);
    if (std::abs(cosHalfangle)>=1)
    {
        return false;
    }
    Vector3D axis = pulses[1].pos - pulses[0].pos;
    if (axis * axis == 0){
        return false;
    }
    newCone = Cone(pulses[0].pos, axis, cosHalfangle,
                   Etot, pulses[0].height);
    return true;
}

Worker::Worker(QObject *parent, const Setup* config_, PlotData* plotdata_) :
    QThread(parent),
    config(config_),
    plotdata(plotdata_)
{
//    stopped=true;
    stopped = false;
    exitted=false;

    enabledChs = EnabledChannel(config->channelSettings);

    infile.setFileName(config->filePath);
//    qDebug()<< outfp << '\n';

    if (config->inputFormat == "BIN")
    {
        if (!infile.open(QIODevice::ReadOnly))
        {
            throw std::invalid_argument("Cannot open file: " + config->filePath.toStdString());
        }
    }
    else if(config->inputFormat == "CSV FULL" || config->inputFormat == "CSV DECODE EVENTS")
    {
        if (!infile.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            throw std::invalid_argument("Cannot open file: " + config->filePath.toStdString());
        }
    }

    QString outfp = config->outputDir.absolutePath() + "/cones.txt";
    outfile.setFileName(outfp);
    if (!outfile.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        throw std::invalid_argument("Cannot open file: " + outfp.toStdString());
    }
    else {
        QTextStream out(&outfile);
        out << qSetFieldWidth(8)
                << "apex.x(cm)" // << "    "
                << "apex.y(cm)" // << "    "
                << "apex.z(cm)" // << "    "
                << "axis.x(cm)" // << "    "
                << "axis.y(cm)" // << "    "
                << "axis.z(cm)" // << "    "
                << qSetFieldWidth(13)
                << "CosHalfAngle"
                << "Etot(keV)"
                << "E_1(keV)" << '\n';
    }
}

void Worker::run()
{
    QTextStream out(&outfile);
    if (config->inputFormat == "BIN")
    {
        QDataStream in(&infile);
        while(!exitted)
        {
            if (!stopped)
            {
                // read some cones
                std::vector<Cone> cones;
                readBIN(cones, in);
                if (exitted)
                    break;
                // update image
                plotdata->updateImage(cones.cbegin(), cones.cend(), true);
                // save cones
                saveCones(cones, out);

                if (exitted || plotdata->counts >= config->maxN)
                    break;
            }
        }
    }
    else if (config->inputFormat == "CSV FULL")
    {
        QTextStream in(&infile);
        while(!exitted)
        {
            if (!stopped)
            {
                // read some cones
                std::vector<Cone> cones;
                readCSVFULL(cones, in);
                if (exitted)
                    break;
                // update image
                plotdata->updateImage(cones.cbegin(), cones.cend(), true);
                // save cones
                saveCones(cones, out);
                if (exitted || plotdata->counts >= config->maxN)
                    break;
            }
        }
    }
    else if (config->inputFormat == "CSV DECODE EVENTS")
    {
        QTextStream in(&infile);
        while(!exitted)
        {
            if (!stopped)
            {
                // read some cones
                std::vector<Cone> cones;
                readCSVDECODEEVENTS(cones, in);
                if (exitted)
                    break;
                // update image
                plotdata->updateImage(cones.cbegin(), cones.cend(), true);
                // save cones
                saveCones(cones, out);
                if (exitted || plotdata->counts >= config->maxN)
                    break;
            }
        }
    }
    // save image & spectrum before quitting
    qDebug() << "Save results before exiting worker thread";
    plotdata->saveOnExit();
    qDebug() << "Worker thread exited.";
}

void Worker::readCSVFULL(std::vector<Cone> &cones, QTextStream &in)
{
    QString line;
    static std::vector<DataCITIROC> packets;
    static double prevTT=0;
    double currTT=0;
    // skip header
    in.readLineInto(&line);
    while (!exitted && cones.size() < config->chuckSize) {
        while (!exitted && cones.size() < config->chuckSize && in.readLineInto(&line)) {
            // split line into data
            QVector<QStringRef> data = line.splitRef(";");
            if(data.size() < 4)
                break;
            int numPackets = data[3].toInt();
            int expectedLen = 102*data[3].toInt() + 4;
            if(data.size() != expectedLen)
                break;

            currTT = data[1].toDouble();
            if (packets.size()>0 && std::abs(currTT - prevTT) >= config->timeWindow){
                processPackets(packets, cones);
                packets.clear();
            }
            prevTT = currTT;

            // parse data
            int startIndex = 4;
            for (int j=0;j<numPackets;j++)
            {
                DataCITIROC newPacket(data.cbegin() + startIndex);
                startIndex += 102;
                // update LG HG Spectra
                plotdata->updateLGSpectra(newPacket.AsicID, newPacket.chargeLG);
                plotdata->updateHGSpectra(newPacket.AsicID, newPacket.chargeHG);

                packets.push_back(std::move(newPacket));
            }
//            qDebug() << newPacket.EventCounter << newPacket.RunEventTimecode << '\n';
        }
        // wait for new data
        if (in.atEnd())
            msleep(1000);
    }
}

void Worker::readCSVDECODEEVENTS(std::vector<Cone> &cones, QTextStream &in)
{
    QString line;
    static std::vector<DataCITIROC> packets;
    static double prevTT=0;
    double currTT=0;
    // skip header
    in.readLineInto(&line);
    while (!exitted && cones.size() < config->chuckSize) {
        while (!exitted && cones.size() < config->chuckSize && in.readLineInto(&line)) {
            // split line into data
            QVector<QStringRef> data = line.splitRef(";");
//            int expectedLen = 103;
            if(data.size() != 103)
                break;

            currTT = data[4].toDouble();
            if (packets.size()>0 && std::abs(currTT - prevTT) >= config->timeWindow){
                processPackets(packets, cones);
                packets.clear();
            }
            prevTT = currTT;

            // parse data
            DataCITIROC newPacket(data.cbegin() + 1);
            // update LG HG Spectra
            plotdata->updateLGSpectra(newPacket.AsicID, newPacket.chargeLG);
            plotdata->updateHGSpectra(newPacket.AsicID, newPacket.chargeHG);
            packets.push_back(std::move(newPacket));
//            qDebug() << newPacket.EventCounter << newPacket.RunEventTimecode << '\n';
        }
        // wait for new data
        if (in.atEnd())
            msleep(1000);
    }
}
//void Worker::readCSV(std::vector<Cone> &cones, QTextStream& in)
//{
//    QString line;
//    std::vector<ulong> histNums;
//    static std::vector<MyPulse> pulses;
//    static double prevTT=0;
//    int i=0;
//    while (!exitted && i < config->chuckSize && in.readLineInto(&line))
//    {
//        MyPulse newPulse = MyPulse(line);
//        auto it = enabledChs.indexOf(newPulse.cellNo);
//        if (it < 0){
//            // cell not found
//            // go to next line
//            continue;
//        }
//        else {
//            newPulse.pos.X = config->channelSettings[it].x;
//            newPulse.pos.Y = config->channelSettings[it].y;
//            newPulse.height *= config->channelSettings[it].caliCoef;
//            // TO DO: calculate z
//        }
//        if (pulses.size()==0 || std::abs(newPulse.timeStamp - prevTT) < config->timeWindow){
//            prevTT = newPulse.timeStamp;
//            pulses.push_back(std::move(newPulse));
//        }
//        else {
//            // process coincident events
//            Cone newCone;
//            if (processCoincidence(pulses, newCone)){
//                histNums.push_back(pulses[0].histNo);
//                cones.push_back(std::move(newCone));
//                i++;
//            }
//            pulses.clear();
//            prevTT = newPulse.timeStamp;
//            pulses.push_back(std::move(newPulse));
//        }
//    }

//    // save cones to file
//    for (int i=0;i<cones.size();i++)
//    {
//        outfile << std::fixed << std::setprecision(2)
//                << std::setw(8) << cones[i].apex.X // << "    "
//                << std::setw(8) << cones[i].apex.Y // << "    "
//                << std::setw(8) << cones[i].apex.Z // << "    "
//                << std::setw(8) << cones[i].axis.X // << "    "
//                << std::setw(8) << cones[i].axis.Y // << "    "
//                << std::setw(8) << cones[i].axis.Z // << "    "
//                << std::fixed << std::setprecision(8)
//                << std::setw(13) << cones[i].cosHalfAngle
//                << std::setw(13) << cones[i].E0
//                << std::setw(13) << cones[i].Edpst
//                << std::setw(13) << histNums[i] << '\n';
//    }

//}

void Worker::readBIN(std::vector<Cone> &cones, QDataStream &in)
{
    // hold a single event
    char buffer[eventSize];
    static std::vector<DataCITIROC> packets;
    static double prevTT=0;
//    int i = 0;
    // discard the garbage data at the begninning
    const uint32_t *p_ui32 = (uint32_t*)&(buffer[0]);
    while (true)
    {
        in.readRawData(buffer, 4);
        uint32_t bword = ((uint32_t)(p_ui32[0]));
        if (((bword >> 4) & 0xc000000) == 0x8000000)
            break;
    }

    int len = 4;
    while (!exitted && cones.size() < config->chuckSize) {
        while (!exitted && cones.size() < config->chuckSize) {
            // read data into buffer
            if (len != eventSize)
            {
                int tmp=in.readRawData(buffer + len, eventSize-len);
                if(tmp < 0)
                    break;
                len += tmp;
            }
            if(len != eventSize)
                break;
            else
                len = 0;
            // parse data
            DataCITIROC newPacket;
            DecodeCITIROCRowEvents(buffer, eventSize, newPacket);
            // update LG HG Spectra
            plotdata->updateLGSpectra(newPacket.AsicID, newPacket.chargeLG);
            plotdata->updateHGSpectra(newPacket.AsicID, newPacket.chargeHG);

            if (packets.size()>0 && std::abs(newPacket.RunEventTimecode_ns - prevTT) >= config->timeWindow){
                processPackets(packets, cones);
                packets.clear();
            }
            prevTT = newPacket.RunEventTimecode_ns;
            packets.push_back(std::move(newPacket));
//            qDebug() << newPacket.EventCounter << newPacket.RunEventTimecode << '\n';
        }
        // wait for new data
        if (in.atEnd())
            msleep(1000);
    }
}

void Worker::processPackets(const std::vector<DataCITIROC>& packets, std::vector<Cone> &cones)
{
    //                if(packets.size() > 2)
    //                    qDebug() << "Coincidence";
    // process coincident events
    std::vector<Event> events;
    if (extractEvents(packets, events))
    {
        // sort by channel id
        std::sort(events.begin(), events.end(),
                  [](const Event & a, const Event & b) -> bool
        {
            return a.id < b.id;
        });
        std::vector<MyPulse> pulses;
        if(event2Pulses(events, pulses))
        {
            // upddate pair spectra
            plotdata->updatePairSpectra(pulses);
            if(config->coincidenceEnabled)
            {
                Cone newCone;
                if (processCoincidence(pulses, newCone)){
                    cones.push_back(std::move(newCone));
                }
            }
        }
    }
}

int Worker::DecodeCITIROCRowEvents(const char* buf, const int bufSize, DataCITIROC& DataCITIROCA)
{
    //Convert char buffer in uin32
    const uint32_t* bufferA = (uint32_t*)&(buf[0]);
    const uint32_t valid_wordA = bufSize/4;

    int DecodedPackets = 0;
    double Time =0;
    double minTime = 0;
    uint32_t i, j, t, s;
    uint datarow[97];

    t= 0;
    s=0;
    while (t < valid_wordA)
    {
        switch (s) {
        case 0:
            if(((bufferA[t] >> 4) & 0xc000000) == 0x8000000)
            {
                s = 1;
                DataCITIROCA.AsicID = (uint16_t) (bufferA[t] & 0xF);
                DataCITIROCA.EventTimecode = ((uint64_t)bufferA[t + 1]);
                DataCITIROCA.RunEventTimecode = (((uint64_t)bufferA[t+ 2]) ) + (((uint64_t)bufferA[t + 3]) << 32);
                DataCITIROCA.EventCounter = ((uint64_t)bufferA[t + 4]);

                DataCITIROCA.EventTimecode_ns = DataCITIROCA.EventTimecode * 25;
                DataCITIROCA.RunEventTimecode_ns =DataCITIROCA.RunEventTimecode * 25;

                t = t + 5;
                minTime = 100000000000000;
            }
            else
                t++;
            break;

        case 1:
            for(i = 0; i < 32; i++)
            {
                uint32_t bword = bufferA[t];
                datarow[i * 3 + 0] = (bword >> 0) & 0x3FFF;
                datarow[i * 3 + 1] = (bword >> 14) & 0x3FFF;
                datarow[i * 3 + 2] = (bword >> 28) & 0x1;
                t++;
            }
            for(i=0;i<32;i++)
            {
                DataCITIROCA.hit[31-i] = (bool)((datarow[i*3+2] & 0x1) == 1 ? true: false);
                int dataHG = (int)datarow[(i * 3) + 0];
                int dataLG= (int)datarow[(i * 3) + 1];
//                if(dataHG > ThresholdSoftware)
//                    DataCITIROCA.chargeHG[i] = (ushort)dataHG;
//                else
//                    DataCITIROCA.chargeHG[i] = 0;
//                if(dataHG > ThresholdSoftware)
//                    DataCITIROCA.chargeLG[i] = (ushort)dataLG;
//                else
//                    DataCITIROCA.chargeLG[i] = 0;
                DataCITIROCA.chargeHG[31-i] = (ushort)dataHG;
                DataCITIROCA.chargeLG[31-i] = (ushort)dataLG;
            }
            s = 2;
            break;

        case 2:
            if((bufferA[t] & 0xc0000000) == 0xc0000000)
            {
//                pC.Enqueue(DataCITIROCA);
                DecodedPackets++;
            }
            t++;
            s = 0;
            break;
        }
    }
    return DecodedPackets;
}


bool Worker::extractEvents(const std::vector<DataCITIROC>& packets, std::vector<Event>& events)
{
    // pick channels with hit = 1 && value > threshold
    for (auto it = packets.cbegin(); it!=packets.cend();it++)
    {
        for (int i=0;i<32;i++)
        {
            if(it->hit[i] == 0)
                continue;
            int id = enabledChs.getID(qMakePair(it->AsicID, i));
            if (id < 0)
                continue;
            Event newEvent;
            double energy;
            if(config->channelSettings[id].source == "LG")
            {
                energy = it->chargeLG[i] * config->channelSettings[id].caliCoef;
                if(energy < config->channelSettings[id].threshold)
                    continue;
                newEvent.energyLG = energy;
                newEvent.energyHG = it->chargeHG[i];
            }
            else if (config->channelSettings[id].source == "HG")
            {
                energy = it->chargeHG[i] * config->channelSettings[id].caliCoef;
                if(energy < config->channelSettings[id].threshold)
                    continue;
                newEvent.energyLG = it->chargeLG[i];
                newEvent.energyHG = energy;
            }
            newEvent.id = id;
            newEvent.AsicID = it->AsicID;
            newEvent.channelNum = i;
            newEvent.EventCounter = it->EventCounter;
            newEvent.RunEventTimecode_ns = it->RunEventTimecode_ns;
            events.push_back(std::move(newEvent));
        }
    }
    if (events.size() == 0)
        return false;
    return true;
}

bool Worker::event2Pulses(const std::vector<Event>& events, std::vector<MyPulse>& pulses)
{
    // find pair and construct pulses
    for (auto it = events.begin(); it != events.end(); it++)
    {
        auto next = it+1;
        if (it->id % 2 == 1)
            continue;
        if (next == events.end())
            continue;
        if (next->id != it->id + 1)
            continue;
        // we have a pair (it, next)
        MyPulse newPulse;
        double e1 = (config->channelSettings[it->id].source == "LG") ? it->energyLG : it->energyHG;
        double e2 = (config->channelSettings[next->id].source == "LG") ? next->energyLG : next->energyHG;
        double z = findZ(e1, e2, config->channelSettings[it->id].z, config->channelSettings[next->id].z);
        newPulse.height = e1 + e2;
        newPulse.pos = Vector3D(config->channelSettings[it->id].x, config->channelSettings[it->id].y, z);
        newPulse.timeStamp = it->RunEventTimecode_ns;
        newPulse.cellNo = it->id / 2;
        newPulse.histNo = it->EventCounter;
        pulses.push_back(std::move(newPulse));
    }
    if(pulses.size() == 0)
        return false;
    std::sort(pulses.begin(), pulses.end(),
              [](const MyPulse & a, const MyPulse & b) -> bool
              {
                  return a.timeStamp < b.timeStamp;
              });
    return true;
}


void Worker::saveCones(const std::vector<Cone>& cones, QTextStream& out)
{
    for (const Cone& p : cones)
    {
        out << qSetFieldWidth(8)
                << QString::number(p.apex.X, 'f', 3) // << "    "
                << QString::number(p.apex.Y, 'f', 3) // << "    "
                << QString::number(p.apex.Z, 'f', 3) // << "    "
                << QString::number(p.axis.X, 'f', 3) // << "    "
                << QString::number(p.axis.Y, 'f', 3) // << "    "
                << QString::number(p.axis.Z, 'f', 3) // << "    "
                << qSetFieldWidth(13)
                << QString::number(p.cosHalfAngle, 'f', 5)
                << QString::number(p.E0, 'f', 5)
                << QString::number(p.Edpst, 'f', 5) << '\n';
    }
}

double Worker::findZ(const double & E1, const double & E2, const double & Z1, const double & Z2)
{
    // linear interpolation
    return (E1 * Z1 + E2 * Z2) / (E1 + E2);
}

void Worker::handleStart()
{
    stopped=false;
}

void Worker::handlePause()
{
    stopped=true;
}

void Worker::handleStop()
{
    exitted = true;
    qDebug() << "Exiting thread signal received.";
}