Многоадресный генератор сигналов - PullRequest
0 голосов
/ 26 октября 2019

Я работаю с устройством DAQ, которое сэмплирует 32 аналоговых канала и отправляет их по протоколу udp с помощью многоадресной рассылки. Я сохраняю все данные, используя ноутбук Ubuntu Linux. В целях разработки я хочу создать генератор синусоидальных волн, который эмулирует DAQ-устройство в localhost .

Как генерировать данные с постоянной частотой и частотой дискретизации? Может быть, я мог бы обрабатывать данные как аудио и используя некоторые существующие утилиты? Или напишите планировщик, который, учитывая частоту дискретизации, размер пакетов и количество каналов, отправляет данные по UDP. Я работаю на Linux с использованием C ++.

1 Ответ

1 голос
/ 29 октября 2019

Я бы просто написал цикл, который будет генерировать пакеты и отправлять их с соответствующим интервалом, например: учитывая:

static constexpr int NUM_CHANNELS = 32;
static constexpr int NUM_SAMPLES_PER_PACKET = 15;

struct packet {
    uint64_t timestamp_ms;
    uint32_t packet_counter;

    int16_t data[NUM_CHANNELS * NUM_SAMPLES_PER_PACKET];
};

Я, очевидно, составил формат пакета, поскольку вы этого не сделалиуказать что угодно. вам нужно взломать это, чтобы соответствовать тому, что на самом деле требуется. Я также использую здесь 16-битные значения, чтобы упростить работу с данными, заполнение 24-битных выборок, вероятно, будет проще всего с массивом uint8_t с и некоторыми побитовыми операциями для упаковки значений.

далееЯ определяю функцию для реализации цикла:

void
generate_samples(int sockfd)
{
    struct timespec scheduler;
    clock_gettime(CLOCK_MONOTONIC, &scheduler);

    struct packet pkt {};
    auto time = 0;

    while (time < 200000) {
        // fill data into packet
        for (auto sample = 0; sample < NUM_SAMPLES_PER_PACKET; ++sample) {
            double t = time++ * (2 * M_PI / 100);
            for (auto channel = 0; channel < NUM_CHANNELS; ++channel) {
                pkt.data[sample * NUM_CHANNELS + channel] = sin(t + 0.1 * channel) * (1<<15);
            }
        }

        // wait until an appropriate amount of time has passed before
        // sending out the next packet
        scheduler.tv_nsec += 75000;
        if(scheduler.tv_nsec >= 1000000000) {
            scheduler.tv_nsec -= 1000000000;
            scheduler.tv_sec++;
        }
        clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &scheduler, NULL);

        struct timespec now;
        clock_gettime(CLOCK_REALTIME, &now);

        // update book-keeping fields
        pkt.timestamp_ms = int64_t{now.tv_sec} * 1000000 + int64_t{now.tv_nsec} / 1000;
        pkt.packet_counter += 1;

        // actually send the packet
        send(sockfd, &pkt, sizeof(pkt), 0);
    }
}

параметр sockfd - это просто дескриптор файла из socket(), который был connect() отредактирован по соответствующему адресу, поэтому send знает, куда отправлятьидти. вам, вероятно, понадобится некоторая проверка ошибок (например, при вызовах clock_* и send), но это делает что-то разумное для меня.

...