kopia lustrzana https://github.com/dgatf/Oscilloscope
400 wiersze
13 KiB
C++
400 wiersze
13 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mainwindow.h"
|
|
#include "ui_mainwindow.h"
|
|
#include "settingsdialog.h"
|
|
|
|
MainWindow::MainWindow(QWidget* parent)
|
|
: QMainWindow(parent)
|
|
, ui(new Ui::MainWindow),
|
|
m_serial(new QSerialPort(this)),
|
|
m_pixmap(new QPixmap(520, 250)),
|
|
m_paint(new QPainter(m_pixmap)),
|
|
m_ui_settings(new SettingsDialog()),
|
|
m_statusLabel(new QLabel())
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
ui->lbPic->setMinimumSize(50, 50);
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
statusBar()->addWidget(m_statusLabel);
|
|
m_statusLabel->setText("Disconnected");
|
|
|
|
ui->cbVdiv->addItem("2V/d");
|
|
ui->cbVdiv->addItem("1V/d");
|
|
ui->cbVdiv->addItem("500mV/d");
|
|
ui->cbVdiv->addItem("100mV/d");
|
|
ui->cbVdiv->setItemData(0, 2000);
|
|
ui->cbVdiv->setItemData(1, 1000);
|
|
ui->cbVdiv->setItemData(2, 500);
|
|
ui->cbVdiv->setItemData(3, 100);
|
|
ui->cbVdiv->setCurrentIndex(1);
|
|
|
|
ui->cbHdiv->addItem("10ms/d");
|
|
ui->cbHdiv->addItem("5ms/d");
|
|
ui->cbHdiv->addItem("1ms/d");
|
|
ui->cbHdiv->addItem("500us/d");
|
|
ui->cbHdiv->addItem("100us/d");
|
|
ui->cbHdiv->setItemData(0, 10000);
|
|
ui->cbHdiv->setItemData(1, 5000);
|
|
ui->cbHdiv->setItemData(2, 1000);
|
|
ui->cbHdiv->setItemData(3, 500);
|
|
ui->cbHdiv->setItemData(4, 100);
|
|
ui->cbHdiv->setCurrentIndex(2);
|
|
|
|
ui->cbTriggerType->addItem("None");
|
|
ui->cbTriggerType->addItem("Rising");
|
|
ui->cbTriggerType->addItem("Falling");
|
|
ui->cbTriggerType->setCurrentIndex(1);
|
|
|
|
QMenu *menu = menuBar()->addMenu(tr("&Menu"));
|
|
|
|
const QIcon connectIcon = QIcon(":/connect.png");
|
|
connectAct = new QAction(connectIcon, tr("&Connect"), this);
|
|
menu->addAction(connectAct);
|
|
|
|
const QIcon disconnectIcon = QIcon(":/disconnect.png");
|
|
disconnectAct = new QAction(disconnectIcon, tr("&Disconnect"), this);
|
|
menu->addAction(disconnectAct);
|
|
menu->addSeparator();
|
|
|
|
const QIcon exportIcon = QIcon(":/save.png");
|
|
QAction *exportAct = new QAction(exportIcon, tr("&Export..."), this);
|
|
menu->addAction(exportAct);
|
|
|
|
const QIcon settingsIcon = QIcon(":/settings.png");
|
|
QAction *settingsAct = new QAction(settingsIcon, tr("&Settings..."), this);
|
|
menu->addAction(settingsAct);
|
|
menu->addSeparator();
|
|
|
|
QAction *aboutAct = new QAction(tr("&About"), this);
|
|
menu->addAction(aboutAct);
|
|
|
|
const QIcon exitIcon = QIcon(":/application-exit.png");
|
|
QAction *exitAct = new QAction(exitIcon, tr("&Exit"), this);
|
|
menu->addAction(exitAct);
|
|
|
|
#ifndef Q_OS_ANDROID
|
|
QToolBar *toolBar = addToolBar(tr("Toolbar"));
|
|
toolBar->addAction(connectAct);
|
|
toolBar->addAction(disconnectAct);
|
|
toolBar->addSeparator();
|
|
toolBar->addAction(exportAct);
|
|
toolBar->addAction(settingsAct);
|
|
toolBar->addSeparator();
|
|
toolBar->addAction(exitAct);
|
|
#endif
|
|
|
|
disconnectAct->setEnabled(false);
|
|
|
|
|
|
connect(m_serial, &QSerialPort::readyRead, this, &MainWindow::readData);
|
|
connect(connectAct, &QAction::triggered, this, &MainWindow::openSerialPort);
|
|
connect(disconnectAct, &QAction::triggered, this, &MainWindow::closeSerialPort);
|
|
connect(settingsAct, &QAction::triggered, this, &MainWindow::settings);
|
|
connect(exportAct, &QAction::triggered, this, &MainWindow::exportData);
|
|
connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
|
|
connect(exitAct, &QAction::triggered, this, &MainWindow::exitApp);
|
|
|
|
connect(ui->cbHdiv, &QComboBox::currentTextChanged, this, &MainWindow::updateHdiv);
|
|
connect(ui->cbVdiv, &QComboBox::currentTextChanged, this, &MainWindow::updateVdiv);
|
|
connect(ui->cbTriggerType, &QComboBox::currentTextChanged, this, &MainWindow::updateTriggerType);
|
|
connect(ui->sbTriggerValue, &QDoubleSpinBox::textChanged, this, &MainWindow::updateTrigger);
|
|
connect(m_ui_settings, &SettingsDialog::accepted, this, &MainWindow::refresh);
|
|
|
|
ui->lbPic->adjustSize();
|
|
drawBackground();
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void MainWindow::readData()
|
|
{
|
|
static bool isTriggered = true;
|
|
static float oldxPos = 0;
|
|
static int16_t oldyPos = m_pixmap->height();
|
|
static uint8_t prevValue = 0;
|
|
static uint8_t deltaRising = 0;
|
|
static uint8_t deltaFalling = 0;
|
|
static uint16_t tsRising = 0;
|
|
static uint16_t tsFalling = 0;
|
|
static uint16_t cont = 0;
|
|
m_paint->setPen(QPen(QColor(m_ui_settings->m_currentSettings.signalcolor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
|
QByteArray data = m_serial->readAll();
|
|
float xDelta = m_pixmap->width() * interval / 1000.0 / timeLenght; // float needed in case delta is less than one
|
|
|
|
for (uint16_t i = 0; i < data.size(); i++) {
|
|
// Find V limits
|
|
if ((uint8_t)data[i] > maxValue) {
|
|
maxValue = data[i];
|
|
}
|
|
|
|
if ((uint8_t)data[i] < minValue) {
|
|
minValue = data[i];
|
|
}
|
|
|
|
// Find triggers and signal properties
|
|
if ((uint8_t)data[i] - prevValue > 0) { // rising
|
|
deltaRising += (uint8_t)data[i] - prevValue;
|
|
deltaFalling = 0;
|
|
}
|
|
|
|
if ((uint8_t)data[i] - prevValue < 0) { // falling
|
|
deltaFalling += prevValue - (uint8_t)data[i];
|
|
deltaRising = 0;
|
|
}
|
|
|
|
prevValue = data[i];
|
|
|
|
if (((float)deltaRising) / 0xFF * 5 > trigger) {
|
|
if ((triggerType == rising || triggerType == none) && cont - tsRising > 2) {
|
|
isTriggered = true;
|
|
rawFreq = cont - tsRising;
|
|
rawDuty = cont - tsFalling;
|
|
}
|
|
|
|
deltaRising = 0;
|
|
deltaFalling = 0;
|
|
tsRising = cont;
|
|
}
|
|
|
|
if (((float)deltaFalling) / 0xFF * 5 > trigger) {
|
|
if (triggerType == falling && cont - tsFalling > 2) {
|
|
isTriggered = true;
|
|
rawFreq = cont - tsFalling;
|
|
rawDuty = cont - tsRising;
|
|
}
|
|
|
|
deltaRising = 0;
|
|
deltaFalling = 0;
|
|
tsFalling = cont;
|
|
}
|
|
|
|
cont++;
|
|
|
|
// Draw new value
|
|
if (isTriggered == true || triggerType == none) {
|
|
float newxPos = oldxPos + xDelta;
|
|
int16_t newyPos = m_pixmap->height() - (uint8_t)data[i] * m_pixmap->height() / 0xFF;
|
|
newyPos = newyPos * 5 / volt + m_pixmap->height() * (6 - 5 / vDiv) / 6 / 2;
|
|
|
|
// End of pixmap
|
|
if (newxPos > m_pixmap->width()) {
|
|
if (triggerType != none) {
|
|
isTriggered = false;
|
|
deltaRising = 0;
|
|
deltaFalling = 0;
|
|
}
|
|
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
|
|
if (pendingExport) {
|
|
if (!m_pixmap->toImage().save(fileName)) {
|
|
statusBar()->showMessage("Export error", 3000);
|
|
} else {
|
|
statusBar()->showMessage("Saved", 3000);
|
|
}
|
|
|
|
pendingExport = false;
|
|
}
|
|
|
|
drawBackground();
|
|
m_paint->setPen(QPen(QColor(m_ui_settings->m_currentSettings.signalcolor), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
|
newxPos = 0;
|
|
maxValue = 0;
|
|
minValue = 0;
|
|
}
|
|
// Draw new value
|
|
else {
|
|
float timePos = (float)ui->slTime->value() / (ui->slTime->maximum() - ui->slTime->minimum()) * 0.25;
|
|
float voltPos = (float)ui->slVolt->value() / (ui->slVolt->maximum() - ui->slVolt->minimum());
|
|
m_paint->drawLine(newxPos + m_pixmap->width() * timePos, newyPos + m_pixmap->height() * voltPos, oldxPos + m_pixmap->width() * timePos, oldyPos + m_pixmap->height() * voltPos);
|
|
}
|
|
|
|
oldxPos = newxPos;
|
|
oldyPos = newyPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::drawBackground()
|
|
{
|
|
uint16_t height = m_pixmap->height();
|
|
uint16_t width = m_pixmap->width();
|
|
m_paint->fillRect(0, 0, width, height, QColor(m_ui_settings->m_currentSettings.backgroundcolor));
|
|
// 0 volts line
|
|
m_paint->setPen(QPen(Qt::red, 1, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
|
|
float voltPos = (float)ui->slVolt->value() / (ui->slVolt->maximum() - ui->slVolt->minimum());
|
|
uint16_t yPos = m_pixmap->height() * (5 / volt + (6 - 5 / vDiv) / 6 / 2);
|
|
m_paint->drawLine(0, yPos + m_pixmap->height() * voltPos, m_pixmap->width(), yPos + m_pixmap->height() * voltPos);
|
|
m_paint->setPen(QColor(m_ui_settings->m_currentSettings.gridcolor));
|
|
|
|
for (uint8_t i = 0; i < 6; i++) { // horizontal lines
|
|
m_paint->drawLine(0, height * i / 6, width, height * i / 6);
|
|
}
|
|
|
|
for (uint8_t i = 0; i < 10; i++) { // vertical lines
|
|
m_paint->drawLine(width * i / 10, 0, width * i / 10, height);
|
|
}
|
|
|
|
QFont font = m_paint->font();
|
|
font.setPixelSize(14);
|
|
//font.setBold(true);
|
|
m_paint->setFont(font);
|
|
m_paint->setPen(QColor(m_ui_settings->m_currentSettings.textcolor));
|
|
m_paint->drawText(5, height - 5, QString::number(vDiv * 1000) + "mV/d " + QString::number((float)timeLenght / 10 * 1000) + "μs/d Vmax=" + QString::number((float)maxValue / 0xFF * 5, 'f',
|
|
3) + " Vmin=" + QString::number((float)minValue / 0xFF * 5, 'f', 3));
|
|
QString freq, duty;
|
|
|
|
if (rawFreq == 0) {
|
|
freq = "0";
|
|
duty = "0";
|
|
} else {
|
|
freq = QString::number((float)1000000 / (rawFreq * interval), 'f', 0);
|
|
duty = QString::number(100 * (float)rawDuty / rawFreq, 'f', 0);
|
|
}
|
|
|
|
m_paint->drawText(5, 20, freq + "Hz " + duty + "%");
|
|
}
|
|
|
|
void MainWindow::resizeEvent(QResizeEvent* event)
|
|
{
|
|
QMainWindow::resizeEvent(event);
|
|
m_paint->end();
|
|
*m_pixmap = m_pixmap->scaled(ui->lbPic->width(), ui->lbPic->height(), Qt::IgnoreAspectRatio);
|
|
m_paint->begin(m_pixmap);
|
|
drawBackground();
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
}
|
|
|
|
void MainWindow::openSerialPort()
|
|
{
|
|
m_serial->setPortName(m_ui_settings->m_currentSettings.name);
|
|
m_serial->setBaudRate(m_ui_settings->m_currentSettings.baudRate);
|
|
m_serial->setDataBits(m_ui_settings->m_currentSettings.dataBits);
|
|
m_serial->setParity(m_ui_settings->m_currentSettings.parity);
|
|
m_serial->setStopBits(m_ui_settings->m_currentSettings.stopBits);
|
|
m_serial->setFlowControl(m_ui_settings->m_currentSettings.flowControl);
|
|
interval = m_ui_settings->m_currentSettings.interval;
|
|
|
|
if (m_serial->open(QIODevice::ReadOnly)) {
|
|
m_statusLabel->setText("Connected (" + m_serial->portName() + " " + QString::number(m_serial->baudRate()) + "bps " + QString::number(interval) + "μs)");
|
|
connectAct->setEnabled(false);
|
|
disconnectAct->setEnabled(true);
|
|
} else {
|
|
statusBar()->showMessage("Connection error", 3000);
|
|
}
|
|
}
|
|
|
|
void MainWindow::closeSerialPort()
|
|
{
|
|
if (m_serial->isOpen()) {
|
|
m_serial->close();
|
|
}
|
|
|
|
statusBar()->showMessage("Disconnected");
|
|
connectAct->setEnabled(true);
|
|
disconnectAct->setEnabled(false);
|
|
}
|
|
|
|
void MainWindow::updateTrigger()
|
|
{
|
|
QString text = ui->sbTriggerValue->text();
|
|
trigger = text.toFloat();
|
|
}
|
|
|
|
void MainWindow::updateTriggerType()
|
|
{
|
|
triggerType = (TriggerType)ui->cbTriggerType->currentIndex();
|
|
}
|
|
|
|
void MainWindow::updateVdiv()
|
|
{
|
|
vDiv = ui->cbVdiv->currentData().toFloat() / 1000;
|
|
volt = vDiv * 6;
|
|
drawBackground();
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
}
|
|
|
|
void MainWindow::updateHdiv()
|
|
{
|
|
|
|
hDiv = ui->cbHdiv->currentData().toFloat() / 1000;
|
|
timeLenght = hDiv * 10;
|
|
drawBackground();
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
}
|
|
|
|
void MainWindow::settings()
|
|
{
|
|
#ifdef Q_OS_ANDROID
|
|
//ui->menuBar->setDisabled(true);
|
|
m_ui_settings->showMaximized();
|
|
#else
|
|
m_ui_settings->move(x() + (width() - m_ui_settings->width()) / 2,
|
|
y() + (height() - m_ui_settings->height()) / 2);
|
|
m_ui_settings->show();
|
|
#endif
|
|
|
|
}
|
|
|
|
void MainWindow::about()
|
|
{
|
|
QMessageBox::about(this, tr("About"),
|
|
tr("<center><b>Oscilloscope Serial </b>v0.1<br>"
|
|
"<br>"
|
|
"DanielGeA, 2021"));
|
|
}
|
|
|
|
void MainWindow::exportData()
|
|
{
|
|
fileName = QFileDialog::getSaveFileName(this,
|
|
tr("Save signal image"), "",
|
|
tr("Bitmap image (*.bmp);;All Files (*)"));
|
|
|
|
if (fileName.isEmpty()) {
|
|
return;
|
|
} else {
|
|
if (m_serial->isOpen()) {
|
|
pendingExport = true;
|
|
} else {
|
|
if (!m_pixmap->toImage().save(fileName)) {
|
|
statusBar()->showMessage("Export error", 3000);
|
|
} else {
|
|
statusBar()->showMessage("Saved", 3000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MainWindow::refresh()
|
|
{
|
|
drawBackground();
|
|
ui->lbPic->setPixmap(*m_pixmap);
|
|
}
|
|
|
|
void MainWindow::exitApp()
|
|
{
|
|
QApplication::quit();
|
|
}
|
|
|
|
|
|
|
|
|