Skip to content

Commit

Permalink
First commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
Amine Mzoughi committed Jul 16, 2019
1 parent e49f947 commit 2f2562f
Show file tree
Hide file tree
Showing 9 changed files with 737 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build*
CMakeLists.txt.user
44 changes: 44 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.0)

project(QwtWaterfallplotExample)

find_package(Qt5Widgets REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Qt5SerialPort REQUIRED)
find_package(Qt5PrintSupport REQUIRED)

# QWT
INCLUDE(FindQwt.cmake)
find_library(Qwt REQUIRED)
include_directories(${QWT_INCLUDE_DIR})

include_directories(${QT_INCLUDES})
include_directories(${QWT_HEADERS})
include_directories(.)

# ==============================================================================
# Source
# ==============================================================================
set(APP_SOURCE main.cpp plot.cpp Waterfallplot.cpp)

# ==============================================================================
# Target
# ==============================================================================
add_executable(qwtwaterfallplot ${APP_SOURCE} ${UISrcs} ${MOCSrcs})

set_target_properties(qwtwaterfallplot PROPERTIES
AUTOMOC TRUE
AUTORCC TRUE
AUTOUIC TRUE)

target_link_libraries(qwtwaterfallplot Qt5::Core Qt5::Gui Qt5::Widgets Qt5::SerialPort Qt5::PrintSupport
${QWT_LIBRARY})

target_include_directories(qwtwaterfallplot PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR})


set_property(TARGET qwtwaterfallplot PROPERTY C_STANDARD 99)
56 changes: 56 additions & 0 deletions FindQwt.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Find Qwt
# ~~~~~~~~
# Copyright (c) 2010, Tim Sutton <tim at linfiniti.com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
# Once run this will define:
#
# QWT_FOUND = system has QWT lib
# QWT_LIBRARY = full path to the QWT library
# QWT_INCLUDE_DIR = where to find headers
#


set(QWT_LIBRARY_NAMES qwt-qt5 qwt6-qt5 qwt qwt6)

find_library(QWT_LIBRARY
NAMES ${QWT_LIBRARY_NAMES}
PATHS
/usr/lib
/usr/local/lib
/usr/local/lib/qt5
"$ENV{LIB_DIR}/lib"
"$ENV{LIB}"
)

set(_qwt_fw)
if(QWT_LIBRARY MATCHES "/qwt.*\\.framework")
string(REGEX REPLACE "^(.*/qwt.*\\.framework).*$" "\\1" _qwt_fw "${QWT_LIBRARY}")
endif()

FIND_PATH(QWT_INCLUDE_DIR NAMES qwt.h PATHS
"${_qwt_fw}/Headers"
/usr/include
/usr/local/include
/usr/local/include/qt5
"$ENV{LIB_DIR}/include"
"$ENV{INCLUDE}"
PATH_SUFFIXES qwt-qt5 qwt qwt6
)

IF (QWT_INCLUDE_DIR AND QWT_LIBRARY)
SET(QWT_FOUND TRUE)
ENDIF (QWT_INCLUDE_DIR AND QWT_LIBRARY)

IF (QWT_FOUND)
FILE(READ ${QWT_INCLUDE_DIR}/qwt_global.h qwt_header)
STRING(REGEX REPLACE "^.*QWT_VERSION_STR +\"([^\"]+)\".*$" "\\1" QWT_VERSION_STR "${qwt_header}")
IF (NOT QWT_FIND_QUIETLY)
MESSAGE(STATUS "Found Qwt: ${QWT_LIBRARY} (${QWT_VERSION_STR})")
ENDIF (NOT QWT_FIND_QUIETLY)
ELSE (QWT_FOUND)
IF (QWT_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find Qwt")
ENDIF (QWT_FIND_REQUIRED)
ENDIF (QWT_FOUND)
210 changes: 210 additions & 0 deletions WaterfallData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#ifndef WATERFALLDATA_H
#define WATERFALLDATA_H

#include <qwt_matrix_raster_data.h>

#include <ctime>

template <class T>
class WaterfallData : public QwtMatrixRasterData
{
public:
WaterfallData(double dXMin, double dXMax, // X bounds
const size_t historyExtent, // will define Y width
const size_t layerPoints) :
m_data(new T[historyExtent * layerPoints]),
m_layerPoints(layerPoints),
m_maxHistoryLength(historyExtent),
m_currentHistoryLength(0),
m_layersTimestamps(new time_t[historyExtent])
{
if (m_layerPoints == 0 || m_maxHistoryLength == 0)
{
throw "Bad usage of WaterfallData !"; // better: call abort();
}

// initialize data with zeroes or the minimal value of T type
clear();

// sanitize
if (dXMin > dXMax)
{
std::swap(dXMin, dXMax);
}

setInterval(Qt::XAxis,
QwtInterval(dXMin, dXMax, QwtInterval::ExcludeMaximum));
setInterval(Qt::YAxis,
QwtInterval(0, m_maxHistoryLength, QwtInterval::ExcludeMaximum));
}

~WaterfallData() override
{
delete [] m_data;
delete [] m_layersTimestamps;
}

// overriden methods
double value(double x, double y) const override
{
const QwtInterval xInterval = interval(Qt::XAxis);
const QwtInterval yInterval = interval(Qt::YAxis);

// + valid
if (!(xInterval.contains(x) && yInterval.contains(y)))
{
return qQNaN();
}

// spacing !
double dx = xInterval.width() / m_layerPoints;
double dy = yInterval.width() / m_maxHistoryLength;

int row = int((y - yInterval.minValue()) / dy);
int col = int((x - xInterval.minValue()) / dx);

if (row >= m_maxHistoryLength)
{
row = m_maxHistoryLength - 1;
}
if (col >= m_layerPoints)
{
col = m_layerPoints - 1;
}

return double(m_data[row * m_layerPoints + col]);
}

/* pixelHint() returns the geometry of a pixel, that can be used
to calculate the resolution and alignment of the plot item, that is
representing the data.
- NearestNeighbour\n
pixelHint() returns the surrounding pixel of the top left value
in the matrix.
- BilinearInterpolation\n
Returns an empty rectangle recommending
to render in target device ( f.e. screen ) resolution.
*/
QRectF pixelHint(const QRectF& area) const override
{
Q_UNUSED(area)

QRectF rect;
if (resampleMode() == NearestNeighbour)
{
const QwtInterval intervalX = interval(Qt::XAxis);
const QwtInterval intervalY = interval(Qt::YAxis);
if (intervalX.isValid() && intervalY.isValid())
{
// spacing in X and Y
const double dx = intervalX.width() / m_layerPoints;
const double dy = intervalY.width() / m_maxHistoryLength;

rect = QRectF(intervalX.minValue(), intervalY.minValue(),
dx, dy);
}
}
return rect;
}

bool addData(const T* const fftData, const size_t length)
{
if (length != m_layerPoints)
{
return false;
}

// another solution is to use move_backward and use m_currentHistoryLength
// the only benefit is to move only the filled layers
// and not all the waterfall layers - 1 when this last is not completely filled !
std::move(m_data + m_layerPoints,
m_data + m_layerPoints + (m_maxHistoryLength - 1) * m_layerPoints,
m_data);

std::copy(fftData, fftData + length, &m_data[m_layerPoints * (m_maxHistoryLength - 1)]);

// do the same for the array of timestamps !
std::move(m_layersTimestamps + 1,
m_layersTimestamps + 1 + (m_maxHistoryLength - 1),
m_layersTimestamps);

time(&m_layersTimestamps[m_maxHistoryLength - 1]);

if (m_currentHistoryLength < m_maxHistoryLength)
{
++m_currentHistoryLength;
}

return true;
}

void clear()
{
std::fill(m_data, m_data + m_layerPoints * m_maxHistoryLength, 0.);
m_currentHistoryLength = 0;

std::fill(m_layersTimestamps, m_layersTimestamps + m_maxHistoryLength, 0);
}

inline size_t getLayerPoints() const { return m_layerPoints; }
inline size_t getMaxHistoryLength() const { return m_maxHistoryLength; }
inline size_t getHistoryLength() const { return m_currentHistoryLength; }

// representation/view data range (may not be equal to the stored data range)
void setRange(double dLower, double dUpper)
{
if (dLower > dUpper)
{
std::swap(dLower, dUpper);
}
setInterval(Qt::ZAxis, QwtInterval(dLower, dUpper));
}

void getRange(double& rangeMin, double& rangeMax) const
{
const QwtInterval& range = interval(Qt::ZAxis);
rangeMin = range.minValue();
rangeMax = range.maxValue();
}

// stored data range !
void getDataRange(double& rangeMin, double& rangeMax) const
{
if (m_currentHistoryLength > 0)
{
auto resultPair = std::minmax_element(
m_data + (m_maxHistoryLength - m_currentHistoryLength) * m_layerPoints,
m_data + m_layerPoints * m_maxHistoryLength);
rangeMin = double(*resultPair.first);
rangeMax = double(*resultPair.second);
}
else
{
rangeMin = rangeMax = 0;
}
}

size_t getCurrentHistoryLength() const { return m_currentHistoryLength; }

time_t getLayerDate(const double y) const
{
const size_t index = y;
if (index < m_maxHistoryLength)
{
return m_layersTimestamps[index];
}
return 0;
}

protected:
T* const m_data;
const size_t m_layerPoints; // fft points
const size_t m_maxHistoryLength; // max number of layers (Y width)
size_t m_currentHistoryLength; // filled layers count

time_t* const m_layersTimestamps;
};

#endif // WATERFALLDATA_H
Loading

0 comments on commit 2f2562f

Please sign in to comment.