////////////////////////////////////////////////////////////////////////////////
//
// OpenHantek
// glgenerator.cpp
//
// Copyright (C) 2010 Oliver Haag
// oliver.haag@gmail.com
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see .
//
////////////////////////////////////////////////////////////////////////////////
#include
#include
#include "glgenerator.h"
#include "dataanalyzer.h"
#include "settings.h"
////////////////////////////////////////////////////////////////////////////////
// class GlArray
/// \brief Initializes the array.
GlArray::GlArray() {
this->data = 0;
this->size = 0;
}
/// \brief Deletes the array.
GlArray::~GlArray() {
if(this->data)
delete this->data;
}
/// \brief Get the size of the array.
/// \return Number of array elements.
unsigned long int GlArray::getSize() {
return this->size;
}
/// \brief Set the size of the array.
/// Previous array contents are lost.
/// \param size New number of array elements.
void GlArray::setSize(unsigned long int size) {
if(this->size == size)
return;
if(this->data)
delete[] this->data;
if(size)
this->data = new GLfloat[size];
else
this->data = 0;
this->size = size;
}
////////////////////////////////////////////////////////////////////////////////
// class GlGenerator
/// \brief Initializes the scope widget.
/// \param settings The target settings object.
/// \param parent The parent widget.
GlGenerator::GlGenerator(DsoSettings *settings, QObject *parent) : QObject(parent) {
this->settings = settings;
this->dataAnalyzer = 0;
this->digitalPhosphorDepth = 0;
this->generateGrid();
}
/// \brief Deletes OpenGL objects.
GlGenerator::~GlGenerator() {
/// \todo Clean up vaChannel
}
/// \brief Set the data analyzer whose data will be drawn.
/// \param dataAnalyzer Pointer to the DataAnalyzer class.
void GlGenerator::setDataAnalyzer(DataAnalyzer *dataAnalyzer) {
if(this->dataAnalyzer)
disconnect(this->dataAnalyzer, SIGNAL(finished()), this, SLOT(generateGraphs()));
this->dataAnalyzer = dataAnalyzer;
connect(this->dataAnalyzer, SIGNAL(finished()), this, SLOT(generateGraphs()));
}
/// \brief Prepare arrays for drawing the data we get from the data analyzer.
void GlGenerator::generateGraphs() {
if(!this->dataAnalyzer)
return;
// Adapt the number of graphs
for(int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
for(int channel = this->vaChannel[mode].count(); channel < this->settings->scope.voltage.count(); ++channel)
this->vaChannel[mode].append(QList());
for(int channel = this->settings->scope.voltage.count(); channel < this->vaChannel[mode].count(); ++channel)
this->vaChannel[mode].removeLast();
}
// Set digital phosphor depth to one if we don't use it
if(this->settings->view.digitalPhosphor)
this->digitalPhosphorDepth = this->settings->view.digitalPhosphorDepth;
else
this->digitalPhosphorDepth = 1;
// Handle all digital phosphor related list manipulations
for(int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
for(int channel = 0; channel < this->vaChannel[mode].count(); ++channel) {
// Resize lists for vector array if the digital phosphor depth has changed
if(this->vaChannel[mode][channel].count() != this->digitalPhosphorDepth)
for(int index = this->vaChannel[mode][channel].count(); index < this->digitalPhosphorDepth; ++index)
this->vaChannel[mode][channel].append(new GlArray());
for(int index = this->digitalPhosphorDepth; index < this->vaChannel[mode][channel].count(); ++index) {
delete this->vaChannel[mode][channel].last();
this->vaChannel[mode][channel].removeLast();
}
// Move the last list element to the front
this->vaChannel[mode][channel].move(this->digitalPhosphorDepth -1, 0);
}
}
this->dataAnalyzer->mutex()->lock();
switch(this->settings->scope.horizontal.format) {
case Dso::GRAPHFORMAT_TY:
// Add graphs for channels
for(int mode = Dso::CHANNELMODE_VOLTAGE; mode < Dso::CHANNELMODE_COUNT; ++mode) {
for(int channel = 0; channel < this->settings->scope.voltage.count(); ++channel) {
// Check if this channel is used and available at the data analyzer
if(((mode == Dso::CHANNELMODE_VOLTAGE) ? this->settings->scope.voltage[channel].used : this->settings->scope.spectrum[channel].used) && this->dataAnalyzer->data(channel) && this->dataAnalyzer->data(channel)->samples.voltage.sample) {
// Check if the sample count has changed
unsigned int neededSize = ((mode == Dso::CHANNELMODE_VOLTAGE) ? this->dataAnalyzer->data(channel)->samples.voltage.count : this->dataAnalyzer->data(channel)->samples.spectrum.count) * 2;
for(int index = 0; index < this->digitalPhosphorDepth; ++index) {
if(this->vaChannel[mode][channel][index]->getSize() != neededSize)
this->vaChannel[mode][channel][index]->setSize(0);
}
// Check if the array is allocated
if(!this->vaChannel[mode][channel].first()->data)
this->vaChannel[mode][channel].first()->setSize(neededSize);
GLfloat *vaNewChannel = this->vaChannel[mode][channel].first()->data;
// What's the horizontal distance between sampling points?
double horizontalFactor;
if(mode == Dso::CHANNELMODE_VOLTAGE)
horizontalFactor = this->dataAnalyzer->data(channel)->samples.voltage.interval / this->settings->scope.horizontal.timebase;
else
horizontalFactor = this->dataAnalyzer->data(channel)->samples.spectrum.interval / this->settings->scope.horizontal.frequencybase;
// Fill vector array
unsigned int arrayPosition = 0;
if(mode == Dso::CHANNELMODE_VOLTAGE) {
for(unsigned int position = 0; position < this->dataAnalyzer->data(channel)->samples.voltage.count; ++position) {
vaNewChannel[arrayPosition++] = position * horizontalFactor - DIVS_TIME / 2;
vaNewChannel[arrayPosition++] = this->dataAnalyzer->data(channel)->samples.voltage.sample[position] / this->settings->scope.voltage[channel].gain + this->settings->scope.voltage[channel].offset;
}
}
else {
for(unsigned int position = 0; position < this->dataAnalyzer->data(channel)->samples.spectrum.count; ++position) {
vaNewChannel[arrayPosition++] = position * horizontalFactor - DIVS_TIME / 2;
vaNewChannel[arrayPosition++] = this->dataAnalyzer->data(channel)->samples.spectrum.sample[position] / this->settings->scope.spectrum[channel].magnitude + this->settings->scope.spectrum[channel].offset;
}
}
}
else {
// Delete all vector arrays
for(int index = 0; index < this->digitalPhosphorDepth; ++index)
this->vaChannel[mode][channel][index]->setSize(0);
}
}
}
break;
case Dso::GRAPHFORMAT_XY:
for(int channel = 0; channel < this->settings->scope.voltage.count(); ++channel) {
// For even channel numbers check if this channel is used and this and the following channel are available at the data analyzer
if(channel % 2 == 0 && channel + 1 < this->settings->scope.voltage.count() && this->settings->scope.voltage[channel].used && this->dataAnalyzer->data(channel) && this->dataAnalyzer->data(channel)->samples.voltage.sample && this->dataAnalyzer->data(channel + 1) && this->dataAnalyzer->data(channel + 1)->samples.voltage.sample) {
// Check if the sample count has changed
unsigned int neededSize = qMin(this->dataAnalyzer->data(channel)->samples.voltage.count, this->dataAnalyzer->data(channel + 1)->samples.voltage.count) * 2;
for(int index = 0; index < this->digitalPhosphorDepth; ++index) {
if(this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]->getSize() != neededSize)
this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]->setSize(0);
}
// Check if the array is allocated
if(!this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].first()->data)
this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].first()->setSize(neededSize);
GLfloat *vaNewChannel = this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel].first()->data;
// Fill vector array
unsigned int arrayPosition = 0;
unsigned int xChannel = channel;
unsigned int yChannel = channel + 1;
for(unsigned int position = 0; position < this->dataAnalyzer->data(channel)->samples.voltage.count; ++position) {
vaNewChannel[arrayPosition++] = this->dataAnalyzer->data(xChannel)->samples.voltage.sample[position] / this->settings->scope.voltage[xChannel].gain + this->settings->scope.voltage[xChannel].offset;
vaNewChannel[arrayPosition++] = this->dataAnalyzer->data(yChannel)->samples.voltage.sample[position] / this->settings->scope.voltage[yChannel].gain + this->settings->scope.voltage[yChannel].offset;
}
}
else {
// Delete all vector arrays
for(int index = 0; index < this->digitalPhosphorDepth; ++index)
this->vaChannel[Dso::CHANNELMODE_VOLTAGE][channel][index]->setSize(0);
}
// Delete all spectrum graphs
for(int index = 0; index < this->digitalPhosphorDepth; ++index)
this->vaChannel[Dso::CHANNELMODE_SPECTRUM][channel][index]->setSize(0);
}
break;
default:
break;
}
this->dataAnalyzer->mutex()->unlock();
emit graphsGenerated();
}
/// \brief Create the needed OpenGL vertex arrays for the grid.
void GlGenerator::generateGrid() {
// Grid
this->vaGrid[0].setSize(((DIVS_TIME * DIVS_SUB - 2) * (DIVS_VOLTAGE - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2) * (DIVS_TIME - 2) - ((DIVS_TIME - 2) * (DIVS_VOLTAGE - 2))) * 2);
int pointIndex = 0;
// Draw vertical lines
for(int div = 1; div < DIVS_TIME / 2; ++div) {
for(int dot = 1; dot < DIVS_VOLTAGE / 2 * DIVS_SUB; ++dot) {
float dotPosition = (float) dot / DIVS_SUB;
this->vaGrid[0].data[pointIndex++] = -div;
this->vaGrid[0].data[pointIndex++] = -dotPosition;
this->vaGrid[0].data[pointIndex++] = -div;
this->vaGrid[0].data[pointIndex++] = dotPosition;
this->vaGrid[0].data[pointIndex++] = div;
this->vaGrid[0].data[pointIndex++] = -dotPosition;
this->vaGrid[0].data[pointIndex++] = div;
this->vaGrid[0].data[pointIndex++] = dotPosition;
}
}
// Draw horizontal lines
for(int div = 1; div < DIVS_VOLTAGE / 2; ++div) {
for(int dot = 1; dot < DIVS_TIME / 2 * DIVS_SUB; ++dot) {
if(dot % DIVS_SUB == 0)
continue; // Already done by vertical lines
float dotPosition = (float) dot / DIVS_SUB;
this->vaGrid[0].data[pointIndex++] = -dotPosition;
this->vaGrid[0].data[pointIndex++] = -div;
this->vaGrid[0].data[pointIndex++] = dotPosition;
this->vaGrid[0].data[pointIndex++] = -div;
this->vaGrid[0].data[pointIndex++] = -dotPosition;
this->vaGrid[0].data[pointIndex++] = div;
this->vaGrid[0].data[pointIndex++] = dotPosition;
this->vaGrid[0].data[pointIndex++] = div;
}
}
// Axes
this->vaGrid[1].setSize((2 + (DIVS_TIME * DIVS_SUB - 2) + (DIVS_VOLTAGE * DIVS_SUB - 2)) * 4);
pointIndex = 0;
// Horizontal axis
this->vaGrid[1].data[pointIndex++] = -DIVS_TIME / 2;
this->vaGrid[1].data[pointIndex++] = 0;
this->vaGrid[1].data[pointIndex++] = DIVS_TIME / 2;
this->vaGrid[1].data[pointIndex++] = 0;
// Vertical axis
this->vaGrid[1].data[pointIndex++] = 0;
this->vaGrid[1].data[pointIndex++] = -DIVS_VOLTAGE / 2;
this->vaGrid[1].data[pointIndex++] = 0;
this->vaGrid[1].data[pointIndex++] = DIVS_VOLTAGE / 2;
// Subdiv lines on horizontal axis
for(int line = 1; line < DIVS_TIME / 2 * DIVS_SUB; ++line) {
float linePosition = (float) line / DIVS_SUB;
this->vaGrid[1].data[pointIndex++] = linePosition;
this->vaGrid[1].data[pointIndex++] = -0.05;
this->vaGrid[1].data[pointIndex++] = linePosition;
this->vaGrid[1].data[pointIndex++] = 0.05;
this->vaGrid[1].data[pointIndex++] = -linePosition;
this->vaGrid[1].data[pointIndex++] = -0.05;
this->vaGrid[1].data[pointIndex++] = -linePosition;
this->vaGrid[1].data[pointIndex++] = 0.05;
}
// Subdiv lines on vertical axis
for(int line = 1; line < DIVS_VOLTAGE / 2 * DIVS_SUB; ++line) {
float linePosition = (float) line / DIVS_SUB;
this->vaGrid[1].data[pointIndex++] = -0.05;
this->vaGrid[1].data[pointIndex++] = linePosition;
this->vaGrid[1].data[pointIndex++] = 0.05;
this->vaGrid[1].data[pointIndex++] = linePosition;
this->vaGrid[1].data[pointIndex++] = -0.05;
this->vaGrid[1].data[pointIndex++] = -linePosition;
this->vaGrid[1].data[pointIndex++] = 0.05;
this->vaGrid[1].data[pointIndex++] = -linePosition;
}
// Border
this->vaGrid[2].setSize(4 * 2);
this->vaGrid[2].data[0] = -DIVS_TIME / 2;
this->vaGrid[2].data[1] = -DIVS_VOLTAGE / 2;
this->vaGrid[2].data[2] = DIVS_TIME / 2;
this->vaGrid[2].data[3] = -DIVS_VOLTAGE / 2;
this->vaGrid[2].data[4] = DIVS_TIME / 2;
this->vaGrid[2].data[5] = DIVS_VOLTAGE / 2;
this->vaGrid[2].data[6] = -DIVS_TIME / 2;
this->vaGrid[2].data[7] = DIVS_VOLTAGE / 2;
}