From 8eba5b0fd30ef70ab0894f44f442646f89d53a49 Mon Sep 17 00:00:00 2001 From: C-Glick <colton.glick@gmail.com> Date: Tue, 29 Mar 2022 04:32:20 -0500 Subject: [PATCH] i need sleep :))))))))) live graphing works better --- crazyflie_groundstation/loggingBlocks.txt | 12 +- groundStation/gui/MicroCART/MicroCART.pro | 6 + groundStation/gui/MicroCART/logworker.cpp | 141 +++++----- groundStation/gui/MicroCART/logworker.h | 11 +- groundStation/gui/MicroCART/mainwindow.cpp | 285 +++++---------------- groundStation/gui/MicroCART/mainwindow.h | 18 +- groundStation/gui/MicroCART/mainwindow.ui | 2 +- 7 files changed, 163 insertions(+), 312 deletions(-) diff --git a/crazyflie_groundstation/loggingBlocks.txt b/crazyflie_groundstation/loggingBlocks.txt index 1affed75f..c0b01846e 100644 --- a/crazyflie_groundstation/loggingBlocks.txt +++ b/crazyflie_groundstation/loggingBlocks.txt @@ -1,10 +1,10 @@ START BLOCK 0 -gyro -30 -gyro.x -gyro.y -gyro.z +stateEstimate +150 +stateEstimate.roll +stateEstimate.pitch +stateEstimate.yaw END BLOCK START BLOCK @@ -32,4 +32,4 @@ pwm pwm.m1_pwm pwm.m2_pwm pwm.m3_pwm -END BLOCK \ No newline at end of file +END BLOCK diff --git a/groundStation/gui/MicroCART/MicroCART.pro b/groundStation/gui/MicroCART/MicroCART.pro index 65705f20d..fda7b62d7 100644 --- a/groundStation/gui/MicroCART/MicroCART.pro +++ b/groundStation/gui/MicroCART/MicroCART.pro @@ -4,10 +4,16 @@ # #------------------------------------------------- +DEFINES += QCUSTOMPLOT_USE_OPENGL + + QT += core gui gamepad greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport +CONFIG += debug + + TARGET = MicroCART TEMPLATE = app diff --git a/groundStation/gui/MicroCART/logworker.cpp b/groundStation/gui/MicroCART/logworker.cpp index 4cdfccdac..2426afe21 100644 --- a/groundStation/gui/MicroCART/logworker.cpp +++ b/groundStation/gui/MicroCART/logworker.cpp @@ -43,92 +43,90 @@ void LogWorker::disconnectBackend() } } -void LogWorker::startLogging(QString filename) { - std::string logFileName = filename.toStdString(); - std::ifstream logFile; +void LogWorker::openLogFile(QString filename){ + logFileName = filename.toStdString(); +} + + +void LogWorker::startLogging() { + keepLogging = true; + logFile.open(logFileName.c_str(), std::fstream::ate); - //std::streamoff p = 0; + std::string line; std::vector<std::string> tokens; std::string temp; std::stringstream stream; - QStringList results; - QString wantedLine; - uint8_t lineCnt = 0; + + QVector<QVector<double>> x; + QVector<QVector<double>> y; + for(int i=0; i<5; i++){ + x.append(QVector<double>()); + y.append(QVector<double>()); + } + + int valCnt = 0; uint8_t nanCnt = 0; - // while(true) { - // logFile.seekg(p); - // while(std::getline(logFile, line)) { - // QString result(line.c_str()); - // qInfo() << result; - // if(logFile.tellg() == -1) { - // p = p + line.size(); - // } - // else { - // p = logFile.tellg(); - // } - // } - // logFile.clear(); - // } - - while(true) { - while(std::getline(logFile, line)) { - //QString result(line.c_str()); - //qInfo() << result; - if(logFlag == 0) { - usleep(1000); + while(keepLogging) { + while(std::getline(logFile, line) && keepLogging) { + + if(line.at(0) == '#') { continue; } - else if(line.at(0) == '#') { - + stream.str(line); + while(getline(stream, temp, '\t')) { + tokens.push_back(temp); } - else if(logFlag == 1) { - lineCnt++; - stream.str(line); - while(getline(stream, temp, '\t')) { - tokens.push_back(temp); + + for(int i = 0; i < 5; i++) { + if(logColumns.at(i) == -1) { + //do not want this column value } - wantedLine.append(tokens.at(0).c_str()); - wantedLine.append(","); - for(int i = 0; i < 5; i++) { - if(logColumns.at(i) == -1) { - wantedLine.append("NULL,"); - } - else { - wantedLine.append(tokens.at(logColumns.at(i)).c_str()); - wantedLine.append(","); - if(!(tokens.at(logColumns.at(i)).compare("nan"))) { - nanCnt++; + else { + //do want this column value + if(!(tokens.at(logColumns.at(i)).compare("nan"))) { + //nan, dont plot + nanCnt++; + }else{ + //contains value that we want to plot + double xVal = std::stod(tokens.at(0)); + double yVal = std::stod(tokens.at(logColumns.at(i))); + //check if y value (variable val) has changed since last value, if so plot it + if(y[i].size() == 0 || y[i].last() != yVal){ + y[i].append(yVal); + //also capture time x + x[i].append(xVal); + valCnt++; } } } - if(nanCnt >= 5) { - nanCnt = 0; - wantedLine = ""; - tokens.clear(); - stream.str(""); - stream.clear(); - lineCnt--; - continue; - } - results.append(wantedLine); - wantedLine = ""; + } + if(nanCnt >= 2) { + nanCnt = 0; tokens.clear(); stream.str(""); stream.clear(); - nanCnt = 0; - if(lineCnt > 8) { - emit(gotLogData(results, false)); - lineCnt = 0; - results.clear(); - } + continue; } - else if(logFlag == 2) { - emit(gotLogData(results, true)); - results.clear(); - logFlag = 0; + tokens.clear(); + stream.str(""); + stream.clear(); + nanCnt = 0; + + //if new values to plot, send to main window + if(valCnt > 2) { + + emit(gotLogData(x, y, false)); + + //clear old values + for(int i=0; i<5; i++){ + x[i].clear(); + y[i].clear(); + } + valCnt = 0; } + } if(!logFile.eof()) { break; @@ -136,4 +134,11 @@ void LogWorker::startLogging(QString filename) { logFile.clear(); usleep(150000); } -} \ No newline at end of file + + logFile.close(); +} + +void LogWorker::stopLogging(){ + this->keepLogging = false; + logFile.clear(); +} diff --git a/groundStation/gui/MicroCART/logworker.h b/groundStation/gui/MicroCART/logworker.h index 667e229c6..d4ebe443b 100644 --- a/groundStation/gui/MicroCART/logworker.h +++ b/groundStation/gui/MicroCART/logworker.h @@ -24,11 +24,12 @@ class LogWorker : public QObject Q_OBJECT public: explicit LogWorker(QObject *parent = nullptr); + void stopLogging(); ~LogWorker(); signals: - void gotLogData(QStringList results, bool lastSet); + void gotLogData(QVector<QVector<double>> x, QVector<QVector<double>> y, bool lastSet); void connected(); void disconnected(); @@ -36,13 +37,17 @@ signals: public slots: void connectBackend(); void disconnectBackend(); - void startLogging(QString filename); + void openLogFile(QString filename); + void startLogging(); private: struct backend_conn * conn; + std::ifstream logFile; + std::string logFileName; + public: - int logFlag = 0; + bool keepLogging = false; std::vector<int8_t> logColumns; }; diff --git a/groundStation/gui/MicroCART/mainwindow.cpp b/groundStation/gui/MicroCART/mainwindow.cpp index 3e5529b4c..7737b9bf5 100644 --- a/groundStation/gui/MicroCART/mainwindow.cpp +++ b/groundStation/gui/MicroCART/mainwindow.cpp @@ -14,6 +14,9 @@ #include <QPixmap> #include <QProcess> #include <QDebug> +#include <algorithm> +#include <QVector> + #include "controlworker.h" #include "crazyflieworker.h" @@ -40,6 +43,7 @@ MainWindow::MainWindow(QWidget *parent) : ui->setupUi(this); qRegisterMetaType<int8_t>("int8_t"); + qRegisterMetaType<QVector<QVector<double>>>("QVector<QVector<double>>"); QGraphicsScene *posScene = new QGraphicsScene(this); @@ -83,17 +87,14 @@ MainWindow::MainWindow(QWidget *parent) : connect(ui->logEntriesComboBox3, SIGNAL (currentTextChanged(QString)), this, SLOT (getLogEntryBoxChange3(QString))); connect(ui->logEntriesComboBox4, SIGNAL (currentTextChanged(QString)), this, SLOT (getLogEntryBoxChange4(QString))); connect(ui->logEntriesComboBox5, SIGNAL (currentTextChanged(QString)), this, SLOT (getLogEntryBoxChange5(QString))); - connect(ui->pb_startLog, SIGNAL (clicked()), this, SLOT (sendStartLog())); - connect(logWorker, SIGNAL (gotLogValues(QStringList)), this, SLOT (graphLogs(QStringList, bool))); connect(ui->pb_logBlockFile, SIGNAL (clicked()), this, SLOT(openLogBlockFile())); connect(ui->pb_logBlockRefresh, SIGNAL (clicked()), this, SLOT(on_pb_lbRefresh())); connect(ui->pb_logBlockStop, SIGNAL (clicked()), this, SLOT(on_pb_lbStop())); connect(ui->pb_logBlockResume, SIGNAL (clicked()), this, SLOT(on_pb_lbResume())); connect(ui->pb_logBlockPause, SIGNAL (clicked()), this, SLOT(on_pb_lbPause())); connect(ui->comboBox_logBlocks, SIGNAL (currentTextChanged(QString)), crazyflieWorker, SLOT (logBlockBoxChanged(QString))); - connect(ui->pb_stopLog, SIGNAL (clicked()), this, SLOT (sendStopLog())); connect(crazyflieWorker, SIGNAL (gotBlockisEnabled(bool)), this, SLOT(setLogButtons(bool))); - connect(logWorker, SIGNAL (gotLogData(QStringList, bool)), this, SLOT(graphLogData(QStringList, bool))); + connect(logWorker, SIGNAL (gotLogData(QVector<QVector<double>>, QVector<QVector<double>>, bool)), this, SLOT(graphLogs(QVector<QVector<double>>, QVector<QVector<double>>, bool))); //connect(ui->pb_logBlockDelete, SIGNAL (clicked()), this, SLOT(on_pb_lbDelete())); connect(this, SIGNAL(sendLogBlockCommand(int8_t, int8_t)), crazyflieWorker, SLOT(frontendLogBlockCommand(int8_t, int8_t))); connect(this, SIGNAL (requestLogBlockID(QString, int8_t)), crazyflieWorker, SLOT(findLogBlockID(QString, int8_t))); @@ -101,7 +102,8 @@ MainWindow::MainWindow(QWidget *parent) : connect(crazyflieWorker, SIGNAL (changeLogBlock_comboBox(QStringList)), SLOT(gotLogBlockNames(QStringList))); connect(crazyflieWorker, SIGNAL (gotActiveLogVariables(QStringList)), this, SLOT(fillLogVariablesBox(QStringList))); connect(crazyflieWorker, SIGNAL (gotLogFile(QString)), this, SLOT (receiveLogFile(QString))); - connect(this, SIGNAL (beginReadingLogFile(QString)), logWorker, SLOT(startLogging(QString))); + connect(this, SIGNAL (beginReadingLogFile(QString)), logWorker, SLOT(openLogFile(QString))); + connect(this, SIGNAL(sendStartLogging()), logWorker, SLOT(startLogging())); connect(this, SIGNAL (rateSetpointSignal(float, float, float, float)), crazyflieWorker, SLOT (setCurrAttRateSetpoint(float, float, float, float))); connect(this, SIGNAL (angleSetpointSignal(float, float, float, float)), crazyflieWorker, SLOT (setCurrAttSetpoint(float, float, float, float))); @@ -160,6 +162,8 @@ MainWindow::MainWindow(QWidget *parent) : cfThread->start(); logThread->start(); + + /* setup logging ui */ ui->pb_getParam->setEnabled(false); ui->pb_setParam->setEnabled(false); ui->pb_logBlockRefresh->setEnabled(false); @@ -176,6 +180,40 @@ MainWindow::MainWindow(QWidget *parent) : logVar4 = "Logging Variable 4"; logVar5 = "Logging Variable 5"; + ui->dataPlot->setOpenGl(true); + + qInfo() << ui->dataPlot->openGl(); + + ui->dataPlot->addGraph(); + ui->dataPlot->addGraph(); + ui->dataPlot->addGraph(); + ui->dataPlot->addGraph(); + ui->dataPlot->addGraph(); + + //set colors and dots + ui->dataPlot->graph(0)->setPen(QPen(Qt::blue, 1)); + ui->dataPlot->graph(0)->setScatterStyle(QCPScatterStyle::ssDisc); + ui->dataPlot->graph(1)->setPen(QPen(Qt::red, 1)); + ui->dataPlot->graph(1)->setScatterStyle(QCPScatterStyle::ssDisc); + ui->dataPlot->graph(2)->setPen(QPen(Qt::green, 1)); + ui->dataPlot->graph(2)->setScatterStyle(QCPScatterStyle::ssDisc); + ui->dataPlot->graph(3)->setPen(QPen(Qt::black, 1)); + ui->dataPlot->graph(3)->setScatterStyle(QCPScatterStyle::ssDisc); + ui->dataPlot->graph(4)->setPen(QPen(Qt::darkMagenta, 1)); + ui->dataPlot->graph(4)->setScatterStyle(QCPScatterStyle::ssDisc); + + ui->dataPlot->xAxis->setLabel("Time (s)"); + ui->dataPlot->yAxis->setLabel("Value"); + ui->dataPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); + + ui->logEntriesComboBox1->setStyleSheet("QComboBox { color: blue; }"); + ui->logEntriesComboBox2->setStyleSheet("QComboBox { color: red; }"); + ui->logEntriesComboBox3->setStyleSheet("QComboBox { color: green; }"); + ui->logEntriesComboBox4->setStyleSheet("QComboBox { color: black; }"); + ui->logEntriesComboBox5->setStyleSheet("QComboBox { color: darkMagenta; }"); + + + /* start gamepad monitor */ gamepadMonitor = new GamepadMonitor(); if(gamepadMonitor->isGamepadConnected()){ @@ -630,219 +668,25 @@ void MainWindow::on_gamepadPitchScale_valueChanged(int arg1) ui->pitchVizBar->setMaximum(std::abs(arg1)); } -void MainWindow::graphLogs(QStringList logs, bool lastSet) -{ - ui->dataPlot->addGraph(); - ui->dataPlot->addGraph(); - ui->dataPlot->addGraph(); - ui->dataPlot->addGraph(); - ui->dataPlot->addGraph(); - - //set colors - ui->dataPlot->graph(0)->setPen(QPen(Qt::blue, 2)); - ui->dataPlot->graph(1)->setPen(QPen(Qt::red, 2)); - ui->dataPlot->graph(2)->setPen(QPen(Qt::green, 2)); - ui->dataPlot->graph(3)->setPen(QPen(Qt::black, 2)); - ui->dataPlot->graph(4)->setPen(QPen(Qt::darkMagenta, 2)); - - ui->logEntriesComboBox1->setStyleSheet("QComboBox { color: blue; }"); - ui->logEntriesComboBox2->setStyleSheet("QComboBox { color: red; }"); - ui->logEntriesComboBox3->setStyleSheet("QComboBox { color: green; }"); - ui->logEntriesComboBox4->setStyleSheet("QComboBox { color: black; }"); - ui->logEntriesComboBox5->setStyleSheet("QComboBox { color: darkMagenta; }"); - - //parse data and put into vector - QVector<double> x0, y0; - QVector<double> x1, y1; - QVector<double> x2, y2; - QVector<double> x3, y3; - QVector<double> x4, y4; - QVector<std::string> tempConv; - - for(int i=0; i<logs.size(); i++) { - tempConv.append(logs.at(i).toStdString()); +void MainWindow::graphLogs(QVector<QVector<double>> x, QVector<QVector<double>> y, bool lastSet) +{ + //set data + for(int i=0; i < 5; i++){ + ui->dataPlot->graph(i)->addData(x[i], y[i], true); } - // qInfo() << QString::fromStdString(tempConv.at(0)) << endl; - // qInfo() << QString::fromStdString(tempConv.at(1)) << endl; - - bool log0 = true; - bool log1 = true; - bool log2 = true; - bool log3 = true; - bool log4 = true; - - for(int i=0; i<logs.size(); i++) { - //use tokenizer here - std::vector <std::string> tokens; - std::stringstream check1(tempConv.at(i)); - std::string intermediate; - while(getline(check1, intermediate, ',')) { - tokens.push_back(intermediate); - } - - // qInfo() << logs.size() << endl; - // qInfo() << "tokens " << i << " :\n " << endl; - // qInfo() << QString::fromStdString(tokens[0]) << endl; - // qInfo() << QString::fromStdString(tokens[1]) << endl; - // qInfo() << QString::fromStdString(tokens[2]) << endl; - // qInfo() << QString::fromStdString(tokens[3]) << endl; - // qInfo() << QString::fromStdString(tokens[4]) << endl; - // qInfo() << QString::fromStdString(tokens[5]) << endl; - - if(i == 0 && std::stod(tokens[0]) < xmin) { - xmin = std::stod(tokens[0]); - } - if(i == logs.size() - 1 && std::stod(tokens[0]) > xmax) { - xmax = std::stod(tokens[0]); - } - - //assign x and y values for log variables - if(tokens[1] == "nan"){ - //do not include point - }else if(tokens[1] == "NULL"){ - //disable this variable if encounter "NULL" - log0 = false; - }else { - x0.append(std::stod(tokens[0])); - y0.append(std::stod(tokens[1])); - if(y0.back()<ymin) { - ymin = y0.back(); - } - if(y0.back()>ymax) { - ymax = y0.back(); - } - } - - if(tokens[2] == "nan"){ - //do not include point - }else if(tokens[2] == "NULL"){ - //disable this variable if encounter "NULL" - log1 = false; - }else { - x1.append(std::stod(tokens[0])); - y1.append(std::stod(tokens[2])); - if(y1.back()<ymin) { - ymin = y1.back(); - } - if(y1.back()>ymax) { - ymax = y1.back(); - } - } - - if(tokens[3] == "nan"){ - //do not include point - }else if(tokens[3] == "NULL"){ - //disable this variable if encounter "NULL" - log2 = false; - }else { - x2.append(std::stod(tokens[0])); - y2.append(std::stod(tokens[3])); - if(y2.back()<ymin) { - ymin = y2.back(); - } - if(y2.back()>ymax) { - ymax = y2.back(); - } - } - if(tokens[4] == "nan"){ - //do not include point - }else if(tokens[4] == "NULL"){ - //disable this variable if encounter "NULL" - log3 = false; - }else { - x3.append(std::stod(tokens[0])); - y3.append(std::stod(tokens[4])); - if(y3.back()<ymin) { - ymin = y3.back(); - } - if(y3.back()>ymax) { - ymax = y3.back(); - } - } - - if(tokens[5] == "nan"){ - //do not include point - }else if(tokens[5] == "NULL"){ - //disable this variable if encounter "NULL" - log4 = false; - }else { - x4.append(std::stod(tokens[0])); - y4.append(std::stod(tokens[5])); - if(y4.back()<ymin) { - ymin = y4.back(); - } - if(y4.back()>ymax) { - ymax = y4[i]; - } + //find max x + double xMax = 0; + for(int i=0; i < x.size(); i++){ + if(!x[i].isEmpty()){ + xMax = (x[i].last() > xMax) ? x[i].last() : xMax; } } - // qInfo() << "yMin: " << ymin << endl; - // qInfo() << "yMax: " << ymax << endl; - // qInfo() << "xMax: " << xmax << endl; - // qInfo() << x0 << endl; - // qInfo() << y0 << endl; - //set data - if(log0) { - if(firstSet) { - ui->dataPlot->graph(0)->setData(x0, y0); - } - else { - ui->dataPlot->graph(0)->addData(x0, y0); - } - } - if(log1) { - if(firstSet) { - ui->dataPlot->graph(1)->setData(x1, y1); - } - else { - ui->dataPlot->graph(1)->addData(x1, y1); - } - } - if(log2) { - if(firstSet) { - ui->dataPlot->graph(2)->setData(x2, y2); - } - else { - ui->dataPlot->graph(2)->addData(x2, y2); - } - } - if(log3) { - if(firstSet) { - ui->dataPlot->graph(3)->setData(x3, y3); - } - else { - ui->dataPlot->graph(3)->addData(x3, y3); - } - } - if(log4) { - if(firstSet) { - ui->dataPlot->graph(4)->setData(x4, y4); - } - else { - ui->dataPlot->graph(4)->addData(x4, y4); - } - } - if(firstSet) { - firstSet = false; - } - - //set lables and boundaries - ui->dataPlot->xAxis->setLabel("Time (s)"); - ui->dataPlot->yAxis->setLabel("Value"); - ui->dataPlot->xAxis->setRange(xmin, xmax); - ui->dataPlot->yAxis->setRange(ymin, ymax); + ui->dataPlot->xAxis->setRange(xMax, 10, Qt::AlignRight); + ui->dataPlot->yAxis->rescale(); ui->dataPlot->replot(); - ui->dataPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); - if(lastSet) { - firstSet = true; - xmin = INFINITY; - xmax = -INFINITY; - ymax = -INFINITY; - ymin = INFINITY; - } } void MainWindow::openLogBlockFile() { @@ -1069,32 +913,31 @@ void MainWindow::getLogEntryBoxChange5(QString text) { } } -void MainWindow::graphLogData(QStringList results, bool lastSet) -{ - graphLogs(results, lastSet); -} - void MainWindow::receiveLogFile(QString filename) { emit(beginReadingLogFile(filename)); } -void MainWindow::sendStartLog() { +void MainWindow::on_pb_startLog_clicked() { ui->pb_stopLog->setEnabled(true); ui->pb_startLog->setEnabled(false); + //clear data from all graphs + for(int i = 0; i < 5; i++){ + ui->dataPlot->graph(i)->data()->clear(); + } + QString name1 = ui->logEntriesComboBox1->currentText(); QString name2 = ui->logEntriesComboBox2->currentText(); QString name3 = ui->logEntriesComboBox3->currentText(); QString name4 = ui->logEntriesComboBox4->currentText(); QString name5 = ui->logEntriesComboBox5->currentText(); logWorker->logColumns = crazyflieWorker->getLogColumns(name1, name2, name3, name4, name5); - logWorker->logFlag = 1; + emit(sendStartLogging()); } -void MainWindow::sendStopLog() { +void MainWindow::on_pb_stopLog_clicked() { ui->pb_startLog->setEnabled(true); ui->pb_stopLog->setEnabled(false); - - logWorker->logFlag = 2; + logWorker->stopLogging(); } diff --git a/groundStation/gui/MicroCART/mainwindow.h b/groundStation/gui/MicroCART/mainwindow.h index c0834c6e1..3479d9e91 100644 --- a/groundStation/gui/MicroCART/mainwindow.h +++ b/groundStation/gui/MicroCART/mainwindow.h @@ -43,6 +43,8 @@ signals: void sendLogBlockCommand(int8_t command, int8_t id); void requestLogBlockID(QString name, int8_t command); void beginReadingLogFile(QString name); + void sendStartLogging(); + void sendStopLogging(); private slots: void on_pbStart_clicked(); @@ -123,7 +125,7 @@ private slots: void on_gamepadPitchScale_valueChanged(int arg1); - void graphLogs(QStringList logs, bool lastSet); + void graphLogs(QVector<QVector<double>> x, QVector<QVector<double>> y, bool lastSet); void openLogBlockFile(); @@ -159,9 +161,8 @@ private slots: //void on_pb_stopLog_clicked(); - void sendStartLog(); - void sendStopLog(); - void graphLogData(QStringList results, bool lastSet); + void on_pb_startLog_clicked(); + void on_pb_stopLog_clicked(); private: Ui::MainWindow *ui; @@ -179,15 +180,6 @@ private: CrazyflieWorker *crazyflieWorker; LogWorker *logWorker; public: - // QVector<double> x0, y0; - // QVector<double> x1, y1; - // QVector<double> x2, y2; - // QVector<double> x3, y3; - // QVector<double> x4, y4; - float xmin = INFINITY; - float xmax = -INFINITY; - float ymax = -INFINITY; - float ymin = INFINITY; bool firstSet = true; }; diff --git a/groundStation/gui/MicroCART/mainwindow.ui b/groundStation/gui/MicroCART/mainwindow.ui index 2df82eecf..9ba58cb47 100644 --- a/groundStation/gui/MicroCART/mainwindow.ui +++ b/groundStation/gui/MicroCART/mainwindow.ui @@ -1435,7 +1435,7 @@ <resources/> <connections/> <buttongroups> - <buttongroup name="buttonGroup_2"/> <buttongroup name="AngleRateButtonGroup"/> + <buttongroup name="buttonGroup_2"/> </buttongroups> </ui> -- GitLab