/**
 * @details
 * Gets the next chunk of data from the UDP socket (if it exists).
 */
void LofarDataSplittingChunker::next(QIODevice* device)
{
    QUdpSocket* socket = static_cast<QUdpSocket*>(device);

    unsigned offsetStream1 = 0;
    unsigned offsetStream2 = 0;
    unsigned prevSeqid = _startTime;
    unsigned prevBlockid = _startBlockid;
    UDPPacket currPacket;
    UDPPacket outputPacket1;
    UDPPacket outputPacket2;
    UDPPacket _emptyPacket1;
    UDPPacket _emptyPacket2;

    WritableData writableData1 = getDataStorage(_nPackets * _packetSizeStream1,
            chunkTypes().at(0));
    WritableData writableData2 = getDataStorage(_nPackets * _packetSizeStream2,
            chunkTypes().at(1));

    unsigned seqid, blockid;
    unsigned totBlocks, lostPackets, diff;
    unsigned packetCounter;

    if (writableData1.isValid() && writableData2.isValid())
    {
        // Loop over the number of UDP packets to put in a chunk.
        for (unsigned i = 0; i < _nPackets; ++i)
        {
            // Chunker sanity check.
            if (!isActive()) return;

            // Wait for datagram to be available.
            while (!socket->hasPendingDatagrams())
                socket->waitForReadyRead(100);

            // Read the current packet from the socket.
            if (socket->readDatagram(reinterpret_cast<char*>(&currPacket), _packetSize) <= 0)
            {
                cerr << "LofarDataSplittingChunker::next(): "
                        "Error while receiving UDP Packet!" << endl;
                i--;
                continue;
            }

            // Check for endianness (Packet data is in little endian format).
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
            // TODO: Convert from little endian to big endian.
            throw QString("LofarDataSplittingChunker: Endianness not supported.");
            seqid   = currPacket.header.timestamp;
            std::cout << seqid << std::endl;
            blockid = currPacket.header.blockSequenceNumber;
#elif Q_BYTE_ORDER == Q_LITTLE_ENDIAN
            seqid   = currPacket.header.timestamp;
            blockid = currPacket.header.blockSequenceNumber;
#endif

            // First time next has been run, initialise startTime and startBlockId.
            if (i == 0 && _startTime == 0) {
                prevSeqid = _startTime = _startTime == 0 ? seqid : _startTime;
                prevBlockid = _startBlockid = _startBlockid == 0 ? blockid : _startBlockid;
            }

            // Sanity check in seqid. If the seconds counter is 0xFFFFFFFF,
            // the data cannot be trusted (ignore).
            if (seqid == ~0U || prevSeqid + 10 < seqid)
            {
                _packetsRejected++;
                i--;
                continue;
            }

            // Check that the packets are contiguous.
            // Block id increments by nrblocks which is defined in the header.
            // Blockid is reset every interval (although it might not start
            // from 0 as the previous frame might contain data from this one).
            totBlocks = (_clock == 160) ?
                    156250 : (prevSeqid % 2 == 0 ? 195313 : 195312);
            lostPackets = 0;
            diff = (blockid >= prevBlockid) ?
                    (blockid - prevBlockid) : (blockid + totBlocks - prevBlockid);

            // Duplicated packets... ignore
            if (diff < _nSamples)
            {
                ++_packetsRejected;
                i -= 1;
                continue;
            }
            // Missing packets
            else if (diff > _nSamples)
            {
                // -1 since it includes this includes the received packet as well
                lostPackets = (diff / _nSamples) - 1;
            }


            if (lostPackets > 0)
            {
                printf("Generate %u empty packets, prevSeq: %u, new Seq: %u, prevBlock: %u, newBlock: %u\n",
                        lostPackets, prevSeqid, seqid, prevBlockid, blockid);
            }

            // TODO
            // BELOW HERE WRITE INTO WRITABLE DATA 1 and 2 correctly.
            //   = Missing packets -> write pair of empty data packets.
            //   = Full packets -> split the packet into the two buffers.
            // =================================================================

            // Generate lostPackets (empty packets) if needed.
            packetCounter = 0;
            for (packetCounter = 0; packetCounter < lostPackets && i + packetCounter < _nPackets; ++packetCounter)
            {
                // Generate empty packet with correct seqid and blockid
                prevSeqid = (prevBlockid + _nSamples < totBlocks) ?
                        prevSeqid : prevSeqid + 1;
                prevBlockid = (prevBlockid + _nSamples) % totBlocks;
                // TODO: probably a cost saving here.
                updateEmptyPacket(_emptyPacket1, prevSeqid, prevBlockid);
                updateEmptyPacket(_emptyPacket2, prevSeqid, prevBlockid);
                offsetStream1 = writePacket(&writableData1, _emptyPacket1, _packetSizeStream1, offsetStream1);
                offsetStream2 = writePacket(&writableData2, _emptyPacket2, _packetSizeStream2, offsetStream2);

                // Check if the number of required packets is reached

                // TODO writePacket(&writableData2, emptyPacket, offset);
            }

            i += packetCounter;

            // Write received packet to 2 streams after updating header and data
            if (i != _nPackets)
            {
                ++_packetsAccepted;

                // Generate Stream 1 packet
                outputPacket1.header = currPacket.header;
                outputPacket1.header.nrBeamlets = _stream1Subbands;
                memcpy((void*)outputPacket1.data, &currPacket.data[_byte1OfStream1], _bytesStream1);
                offsetStream1 = writePacket(&writableData1, outputPacket1, _packetSizeStream1, offsetStream1);

                // Generate Stream 2 packet
                //--------------------------------------------------------------
                outputPacket2.header = currPacket.header;
                outputPacket2.header.nrBeamlets = _stream2Subbands;
                memcpy((void*)outputPacket2.data, &currPacket.data[_byte1OfStream2], _bytesStream2);
                offsetStream2 = writePacket(&writableData2, outputPacket2, _packetSizeStream2, offsetStream2);

                prevSeqid = seqid;
                prevBlockid = blockid;
            }
        }
    }

    else {
        // Must discard the datagram if there is no available space.
        if (!isActive()) return;
        socket->readDatagram(0, 0);
        cout << "LofarDataSplittingChunker::next(): "
                "Writable data not valid, discarding packets." << endl;
    }

    // Update _startTime
    _startTime = prevSeqid;
    _startBlockid = prevBlockid;
}
/**
 * @details
 * Constructor
 *
 * XML options.
 * ================================
 *
 * Read from base class:
 *  - data (type)  LIST.
 *  - connection (host, port)
 *
 * Class Specific options:
 *  - samplesPerPacket (value)
 *  - subbandsPerPacket (value)
 *  - nRawPolarisations (value)
 *
 *  - udpPacketsPerIteration (value)
 *  - clock (value)
 *  - dataBitSize (value)
 *
 */
LofarDataSplittingChunker::LofarDataSplittingChunker(const ConfigNode& config)
: AbstractChunker(config)
{
    // Check the configuration type matches the class name.
    if (config.type() != "LofarDataSplittingChunker")
        throw _err("LofarDataSplittingChunker(): "
                "Invalid or missing XML configuration.");

    // Retrieve configuration options.
    // -------------------------------------------------------------------------
    // Packet dimensions.
    _nSamples = config.getOption("samplesPerPacket", "value").toUInt();
    // Total number of subbands per incoming packet
    _nSubbands = config.getOption("subbandsPerPacket", "value").toUInt();
    // Details of two streams to process, split into subband ranges
    _stream1SubbandStart = config.getOption("Stream1", "subbandStart").toUInt();
    _stream1SubbandEnd = config.getOption("Stream1", "subbandEnd").toUInt();
    _stream2SubbandStart = config.getOption("Stream2", "subbandStart").toUInt();
    _stream2SubbandEnd = config.getOption("Stream2", "subbandEnd").toUInt();

    _nPolarisations = config.getOption("nRawPolarisations", "value").toUInt();
    // Number of UDP packets collected into one chunk (iteration of the pipeline).
    _nPackets = config.getOption("udpPacketsPerIteration", "value").toUInt();
    // Clock => sample rate.
    _clock = config.getOption("clock", "value").toUInt();

    // Calculate the packet data size and the size of the packets for
    // two output streams
    _stream1Subbands = _stream1SubbandEnd - _stream1SubbandStart + 1;
    _stream2Subbands = _stream2SubbandEnd - _stream2SubbandStart + 1;
    if (_stream1SubbandEnd > _nSubbands || _stream2SubbandEnd > _nSubbands)
        throw _err("Subband ranges exceed number of subbands");


    size_t headerSize = sizeof(struct UDPPacket::Header);
    _packetSize = _nSubbands * _nSamples * _nPolarisations;
    // And the output streams
    _packetSizeStream1 = _stream1Subbands * _nSamples * _nPolarisations;
    _packetSizeStream2 = _stream2Subbands * _nSamples * _nPolarisations;
    unsigned sampleBits = config.getOption("dataBitSize", "value").toUInt();
    switch (sampleBits)
    {
        case 8:
            _packetSize = _packetSize * sizeof(TYPES::i8complex) + headerSize;
            _packetSizeStream1 = _packetSizeStream1 * sizeof(TYPES::i8complex) + headerSize;
            _packetSizeStream2 = _packetSizeStream2 * sizeof(TYPES::i8complex) + headerSize;
            _bytesStream1 = _packetSizeStream1 - headerSize;
            _bytesStream2 = _packetSizeStream2 - headerSize;
            _byte1OfStream1 = _stream1SubbandStart * _nSamples * _nPolarisations * sizeof(TYPES::i8complex);
            _byte1OfStream2 = _stream2SubbandStart * _nSamples * _nPolarisations * sizeof(TYPES::i8complex);
            break;
        case 16:
            _packetSize = _packetSize * sizeof(TYPES::i16complex) + headerSize;
            _packetSizeStream1 = _packetSizeStream1 * sizeof(TYPES::i16complex) + headerSize;
            _packetSizeStream2 = _packetSizeStream2 * sizeof(TYPES::i16complex) + headerSize;
            _bytesStream1 = _packetSizeStream1 - headerSize;
            _bytesStream2 = _packetSizeStream2 - headerSize;
            _byte1OfStream1 = _stream1SubbandStart * _nSamples * _nPolarisations * sizeof(TYPES::i16complex);
            _byte1OfStream2 = _stream2SubbandStart * _nSamples * _nPolarisations * sizeof(TYPES::i16complex);
            break;
        default:
            throw _err("LofarDataSplittingChunker(): "
                    "Unsupported number of data bits.");
    }

    // Initialise class variables.
    _startTime = _startBlockid = 0;
    _packetsAccepted = 0;
    _packetsRejected = 0;

    // Check a number of data chunk types are registered to be written.
    // These are set in the XML.
    if (type().isEmpty())
        throw _err("LofarDataSplittingChunker(): Data type unspecified.");

    if (chunkTypes().size() != 2)
        throw _err("LofarDataSplittingChunker(): "
                "Chunk types missing, expecting 2.");

    // Set the empty packet data for stream 1
    memset((void*)_emptyPacket1.data, 0, _bytesStream1);
    _emptyPacket1.header.nrBeamlets = _stream1Subbands;
    _emptyPacket1.header.nrBlocks = _nSamples;

    // Set the empty packet data for stream 2
    memset((void*)_emptyPacket2.data, 0, _bytesStream2);
    _emptyPacket2.header.nrBeamlets = _stream2Subbands;
    _emptyPacket2.header.nrBlocks = _nSamples;
}
Esempio n. 3
0
// Gets the next chunk of data from the UDP socket (if it exists).
void K7Chunker::next(QIODevice* device)
{
    unsigned int i;
    unsigned int lostPackets, difference;
    unsigned int offsetStream;
    unsigned int lostPacketCounter;
    unsigned long int previousTimestamp;
    unsigned int previousAccumulation;
    unsigned long int timestamp;
    unsigned int accumulation;
    unsigned int rate;
    QUdpSocket* udpSocket = static_cast<QUdpSocket*>(device);
    K7Packet currentPacket;
    K7Packet outputPacket;
    K7Packet _emptyPacket;

    difference = 0;
    offsetStream = 0;
    timestamp = 0;
    accumulation = 0;
    rate = 0;
    previousTimestamp = _startTimestamp;
    previousAccumulation = _startAccumulation;

    // Get writable buffer space for the chunk.
    WritableData writableData;
    if ( ! _writable.isValid() )
    {
        writableData = getDataStorage(_nPackets * _packetSizeStream, chunkTypes().at(0));
    }
    else
    {
        writableData = _writable;
    }

    if (writableData.isValid())
    {
        // Loop over the number of UDP packets to put in a chunk.
        for (i = 0; i < _nPackets; ++i)
        {
            // Chunker sanity check.
            if (!isActive())
                return;

            // Wait for datagram to be available.
            while ( !udpSocket->hasPendingDatagrams() )
            {
                _writable = writableData;
                continue;
            }

            // Read the current UDP packet from the socket.
            if (udpSocket->readDatagram(reinterpret_cast<char*>(&currentPacket), _packetSize) <= 0)
            {
                std::cerr << "K7Chunker::next(): Error while receiving UDP Packet!" << std::endl;
                i--;
                continue;
            }

            // Get the UDP packet header.
            timestamp    = currentPacket.header.UTCtimestamp;
            accumulation = currentPacket.header.accumulationNumber;
            rate         = currentPacket.header.accumulationRate;
            //std::cout << "K7Chunker::next(): timestamp " << timestamp << " accumulation " << accumulation << " rate " << rate << std::endl;
            // First time next() has been run, initialise _startTimestamp and _startAccumulation.
            if (i == 0 && _startTimestamp == 0)
            {
                previousTimestamp = _startTimestamp = _startTimestamp == 0 ? timestamp : _startTimestamp;
                previousAccumulation = _startAccumulation = _startAccumulation == 0 ? accumulation : _startAccumulation;
                //std::cout << "K7Chunker::next(): timestamp " << timestamp << " accumulation " << accumulation << " rate " << rate << std::endl;
            }

            // Sanity check in UTCtimestamp. If the seconds counter is 0xFFFFFFFFFFFFFFFF, the data cannot be trusted (ignore).
            if (timestamp == ~0UL || previousTimestamp + 10 < timestamp)
            {
                std::cerr << "K7Chunker::next(): Data cannot be trusted! Timestamp is " << timestamp << " or previousTimestamp is " << previousTimestamp << std::endl;
                exit(-1);
            }

            // Check that the packets are contiguous. accumulationNumber increments by
            // accumulationRate which is defined in the header of UDP packet.
            // accumulationNumber is reset every interval (although it might not start from 0
            // as the previous frame might contain data from this one).
            lostPackets = 0;
            difference = (accumulation >= previousAccumulation) ? (accumulation - previousAccumulation) : (accumulation + _packetsPerSecond - previousAccumulation);
#if 0
            // Duplicated packets. Need to address this. ICBF does not duplicate packets though.
            if (difference < rate)
            {
                std::cout << "Duplicated packets, difference " << difference << std::endl;
                ++_packetsRejected;
                i -= 1;
                continue;
            }
            else
#endif
            // Missing packets.
            if (difference > rate)
            {
                // -1 since it includes the received packet as well.
                lostPackets = (difference / rate) - 1;
            }

            if (lostPackets > 0)
            {
                printf("K7Chunker::next(): generate %u empty packets, previousTimestamp: %lu, new timestamp: %lu, prevAccumulation: %u, newAccumulation: %u\n",
                        lostPackets, previousTimestamp, timestamp, previousAccumulation, accumulation);
            }

            // Generate lostPackets (empty packets) if needed.
            lostPacketCounter = 0;
            for (lostPacketCounter = 0; lostPacketCounter < lostPackets && i + lostPacketCounter < _nPackets; ++lostPacketCounter)
            {
                // Generate empty packet with correct timestamp and accumulation number.
                previousTimestamp = (previousAccumulation < _packetsPerSecond) ? previousTimestamp : previousTimestamp + 1;
                previousAccumulation = previousAccumulation % _packetsPerSecond;
                updateEmptyPacket(_emptyPacket, previousTimestamp, previousAccumulation, rate);
                offsetStream = writePacket(&writableData, _emptyPacket, _packetSizeStream, offsetStream);
            }
            i += lostPacketCounter;

            // Write received packet to stream after updating header and data.
            if (i != _nPackets)
            {
                ++_packetsAccepted;
                // Generate stream packet.
                outputPacket.header = currentPacket.header;
                memcpy((void*)outputPacket.data, &currentPacket.data[_byte1OfStream], _bytesStream);
                offsetStream = writePacket(&writableData, outputPacket, _packetSizeStream, offsetStream);
                previousTimestamp = timestamp;
                previousAccumulation = accumulation;
            }
        }
        _chunksProcessed++;
        _chunkerCounter++;
        // Clear any data locks.
        _writable = WritableData();
        if (_chunkerCounter % 100 == 0)
        {
            std::cout << "K7Chunker::next(): " << _chunksProcessed << " chunks processed. " << "UTC timestamp " << timestamp << " accumulationNumber " << accumulation << " accumulationRate " << rate << std::endl;
        }
    }
    else
    {
        // Must discard the datagram if there is no available space.
        if (!isActive()) return;
        udpSocket->readDatagram(0, 0);
        std::cout << "K7Chunker::next(): Writable data not valid, discarding packets." << std::endl;
    }

    // Update _startTime.
    _startTimestamp = previousTimestamp;
    _startAccumulation = previousAccumulation;
}