mirror of
https://github.com/KuskoSoft/FeitCSI.git
synced 2026-08-27 14:18:34 +03:00
plot with cairo, remove gnuplot
This commit is contained in:
parent
2086412fbf
commit
0f94a0f1cb
@ -87,7 +87,7 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkNotebook">
|
||||
<object class="GtkNotebook" id="leftBar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<child>
|
||||
@ -819,9 +819,16 @@
|
||||
<property name="can-focus">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSocket" id="chartSocket">
|
||||
<object class="GtkBox" id="plotBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
|
||||
@ -23,6 +23,8 @@
|
||||
#include "WiFIController.h"
|
||||
#include "PacketInjector.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/Plot.h"
|
||||
#include "Csi.h"
|
||||
#include "UdpSocket.h"
|
||||
#include <thread>
|
||||
|
||||
@ -32,8 +34,16 @@ class MainController
|
||||
public:
|
||||
inline static UdpSocket *udpSocket = nullptr;
|
||||
|
||||
Plot *plotAmplitude;
|
||||
|
||||
Plot *plotPhase;
|
||||
|
||||
static MainController *getInstance();
|
||||
|
||||
|
||||
static gint updatePlots();
|
||||
|
||||
void initPlots();
|
||||
|
||||
static void deleteInstance();
|
||||
|
||||
void runNoGui(bool detach = false);
|
||||
@ -59,6 +69,12 @@ private:
|
||||
|
||||
inline static MainWindow *mainWindow = nullptr;
|
||||
|
||||
Glib::RefPtr<Gtk::Application> app;
|
||||
|
||||
inline static Csi *csiToPlot = nullptr;
|
||||
|
||||
guint updatePlotsSourceId = 0;
|
||||
|
||||
pthread_t measureCsiThread = 0;
|
||||
|
||||
pthread_t injectPacketThread = 0;
|
||||
@ -74,6 +90,10 @@ private:
|
||||
static void intHandler(int dummy);
|
||||
|
||||
static void *injectPackets(void *arg);
|
||||
|
||||
bool measuring = false;
|
||||
|
||||
bool injecting = false;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -20,7 +20,9 @@
|
||||
#define WIFI_CSI_CONTROLLER_H
|
||||
|
||||
#include "Netlink.h"
|
||||
#include "GnuPlot.h"
|
||||
#include "Csi.h"
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
#define IWL_MVM_VENDOR_ATTR_CSI_HDR 0x4d
|
||||
#define IWL_MVM_VENDOR_ATTR_CSI_DATA 0x4e
|
||||
@ -33,6 +35,8 @@ public:
|
||||
void init();
|
||||
int listenToCsi();
|
||||
void enableCsi(bool enable = true);
|
||||
inline static std::mutex csiQueueMutex;
|
||||
inline static std::queue<Csi*> csiQueue;
|
||||
|
||||
~WiFiCsiController();
|
||||
|
||||
@ -40,7 +44,6 @@ private:
|
||||
static int listenToCsiHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
||||
static int processListenToCsiHandler(nl_msg *msg, void *arg);
|
||||
static void printDetail(Csi *c);
|
||||
GnuPlot gnuPlot;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -21,7 +21,6 @@
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include "CsiProcessor.h"
|
||||
#include "GnuPlot.h"
|
||||
|
||||
class CsiProcessingWindow
|
||||
{
|
||||
@ -55,7 +54,7 @@ private:
|
||||
void processingSaveGtkButtonClicked();
|
||||
|
||||
CsiProcessor csiProcessor;
|
||||
GnuPlot gnuPlot;
|
||||
|
||||
uint32_t currentIndex = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
#define MAIN_WINDOW_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include "gui/GnuPlot.h"
|
||||
#include "gui/Plot.h"
|
||||
#include "gui/CsiProcessingWindow.h"
|
||||
|
||||
class MainWindow : public Gtk::ApplicationWindow
|
||||
@ -31,6 +31,7 @@ public:
|
||||
MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &builder);
|
||||
void fatalError(std::string msg);
|
||||
|
||||
Glib::RefPtr<Gtk::Builder> builder;
|
||||
|
||||
private:
|
||||
int chartWidth = 0;
|
||||
@ -44,7 +45,6 @@ private:
|
||||
|
||||
std::map<std::string, std::string> errors;
|
||||
Glib::RefPtr<Gtk::Adjustment> consoleScrollbarAdjustment;
|
||||
Glib::RefPtr<Gtk::Builder> builder;
|
||||
Glib::RefPtr<Gtk::MessageDialog> errorDialog;
|
||||
|
||||
Glib::RefPtr<Gtk::Button> measureButton;
|
||||
@ -67,8 +67,7 @@ private:
|
||||
Glib::RefPtr<Gtk::TextView> console;
|
||||
Glib::RefPtr<Gtk::Label> errorMessages;
|
||||
|
||||
Glib::RefPtr<Gtk::Socket> chartSocket;
|
||||
GnuPlot gnuPlot;
|
||||
Plot plot;
|
||||
|
||||
void closeButton();
|
||||
|
||||
@ -103,9 +102,9 @@ private:
|
||||
void injectRepeatChange();
|
||||
|
||||
void updateErrorMessages();
|
||||
|
||||
|
||||
void measureButtonClicked();
|
||||
|
||||
|
||||
void injectButtonClicked();
|
||||
|
||||
void updateSensitivity();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||
* Copyright (C) 2024 Miroslav Hutar.
|
||||
*
|
||||
* 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
|
||||
@ -16,30 +16,41 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GNU_PLOT_H
|
||||
#define GNU_PLOT_H
|
||||
#ifndef PLOT_H
|
||||
#define PLOT_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <gtkmm/socket.h>
|
||||
#include <Csi.h>
|
||||
#include <string>
|
||||
#include "Csi.h"
|
||||
#include <future>
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <string>
|
||||
#include <gtkmm.h>
|
||||
#include <mutex>
|
||||
|
||||
class GnuPlot
|
||||
class Plot : public Gtk::DrawingArea
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void setWindow(uint64_t id);
|
||||
void setBlank();
|
||||
void reload();
|
||||
void updateChartAsync(Csi *csi);
|
||||
|
||||
private:
|
||||
inline static std::string lastCmd;
|
||||
inline static FILE *gnuPlotPipe;
|
||||
inline static std::future<void> runningAsyncEvent;
|
||||
void init(Glib::RefPtr<Gtk::Box> box);
|
||||
void updateData(Csi *csi, std::vector<double> *data);
|
||||
double yTicksMax = 200;
|
||||
double yTicksMin = 0;
|
||||
std::string yLabel = "";
|
||||
std::string xLabel = "Subcarrier index";
|
||||
std::string title = "";
|
||||
|
||||
void updateChart(Csi *csi);
|
||||
private:
|
||||
const double colors[4][3] = {
|
||||
{ 1.0, 0.0, 0.0 },
|
||||
{ 0.0, 0.0, 1.0 },
|
||||
{ 1.0, 0.7, 0.0 },
|
||||
{ 0.0, 1.0, 0.0 },
|
||||
};
|
||||
Csi *csi;
|
||||
std::vector<double> *data;
|
||||
std::mutex updateDataMutex;
|
||||
double yTicks;
|
||||
|
||||
bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr);
|
||||
};
|
||||
|
||||
#endif
|
||||
5945
include/layout.h
5945
include/layout.h
File diff suppressed because it is too large
Load Diff
@ -42,7 +42,7 @@ void Arguments::init()
|
||||
.injectDelay = 100000,
|
||||
.injectRepeat = 0,
|
||||
.coding = "LDPC",
|
||||
.format = "NOHT",
|
||||
.format = "HT",
|
||||
.inject = false,
|
||||
.measure = true,
|
||||
.mode = "measure",
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
#include "CsiProcessor.h"
|
||||
#include "main.h"
|
||||
#include "Logger.h"
|
||||
#include "GnuPlot.h"
|
||||
#include "interpolation.h"
|
||||
#include "Arguments.h"
|
||||
|
||||
|
||||
@ -39,20 +39,124 @@ MainController *MainController::getInstance()
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
gint MainController::updatePlots()
|
||||
{
|
||||
WiFiCsiController::csiQueueMutex.lock();
|
||||
|
||||
if (!WiFiCsiController::csiQueue.empty())
|
||||
{
|
||||
if (csiToPlot)
|
||||
{
|
||||
delete csiToPlot;
|
||||
}
|
||||
|
||||
csiToPlot = WiFiCsiController::csiQueue.front();
|
||||
WiFiCsiController::csiQueue.pop();
|
||||
}
|
||||
|
||||
//clear old values
|
||||
while (!WiFiCsiController::csiQueue.empty()) {
|
||||
delete WiFiCsiController::csiQueue.front();
|
||||
WiFiCsiController::csiQueue.pop();
|
||||
}
|
||||
|
||||
WiFiCsiController::csiQueueMutex.unlock();
|
||||
|
||||
if (!csiToPlot)
|
||||
{
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
MainController *mainController = MainController::getInstance();
|
||||
|
||||
mainController->plotAmplitude->updateData(csiToPlot, &csiToPlot->magnitude);
|
||||
mainController->plotPhase->updateData(csiToPlot, &csiToPlot->phase);
|
||||
|
||||
// force refresh, not wait on next frame redraw
|
||||
/* GtkAllocation reg;
|
||||
gtk_widget_get_allocation((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj(), ®);
|
||||
cairo_region_t *creg = cairo_region_create_rectangle(®);
|
||||
GdkWindow *pUnderlyingWindow = gtk_widget_get_window((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj());
|
||||
cairo_t *cr = gdk_cairo_create(pUnderlyingWindow);
|
||||
gdk_cairo_region(cr, creg);
|
||||
cairo_clip(cr);
|
||||
|
||||
gtk_widget_draw((GtkWidget *)mainController->plotAmplitude->get_parent()->gobj(), cr);
|
||||
cairo_destroy(cr);
|
||||
cairo_region_destroy(creg); */
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
void MainController::initPlots()
|
||||
{
|
||||
Glib::RefPtr<Gtk::Box> plotBox = Glib::RefPtr<Gtk::Box>::cast_dynamic(this->mainWindow->builder->get_object("plotBox"));
|
||||
MainController *mainController = MainController::getInstance();
|
||||
mainController->plotAmplitude->yLabel = "Amplitude";
|
||||
mainController->plotAmplitude->title = "Amplitude";
|
||||
mainController->plotAmplitude->init(plotBox);
|
||||
mainController->plotPhase->yLabel = "Phase (rad)";
|
||||
mainController->plotPhase->title = "Phase";
|
||||
mainController->plotPhase->yTicksMin = -4;
|
||||
mainController->plotPhase->yTicksMax = 4;
|
||||
mainController->plotPhase->init(plotBox);
|
||||
this->updatePlotsSourceId = gdk_threads_add_idle((GSourceFunc)MainController::updatePlots, nullptr);
|
||||
}
|
||||
|
||||
void MainController::deleteInstance()
|
||||
{
|
||||
MainController *mainController = MainController::INSTANCE;
|
||||
if (mainController->measuring)
|
||||
{
|
||||
MainController::INSTANCE->measureCsi(true);
|
||||
}
|
||||
if (mainController->injecting)
|
||||
{
|
||||
MainController::INSTANCE->injectPackets(true);
|
||||
}
|
||||
if (Arguments::arguments.plot)
|
||||
{
|
||||
MainController::INSTANCE->app->quit();
|
||||
}
|
||||
|
||||
delete MainController::INSTANCE;
|
||||
}
|
||||
|
||||
void MainController::runNoGui(bool detach)
|
||||
{
|
||||
this->initInterface();
|
||||
if (Arguments::arguments.plot)
|
||||
{
|
||||
gtk_init(NULL, NULL);
|
||||
Glib::init();
|
||||
this->plotAmplitude = new Plot();
|
||||
this->plotPhase = new Plot();
|
||||
this->app = Gtk::Application::create("com.kuskosoft.feitcsi");
|
||||
std::string layout((char *)MainWindow_glade);
|
||||
//auto builder = Gtk::Builder::create_from_file("MainWindow.glade");
|
||||
auto builder = Gtk::Builder::create_from_string(layout);
|
||||
builder->get_widget_derived("MainWindow", this->mainWindow);
|
||||
this->initPlots();
|
||||
if (Arguments::arguments.measure)
|
||||
{
|
||||
this->measureCsi();
|
||||
}
|
||||
if (Arguments::arguments.inject)
|
||||
{
|
||||
this->injectPackets();
|
||||
}
|
||||
this->app->run(*this->mainWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Arguments::arguments.measure)
|
||||
{
|
||||
this->measuring = true;
|
||||
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
||||
}
|
||||
if (Arguments::arguments.inject)
|
||||
{
|
||||
this->injecting = true;
|
||||
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
||||
}
|
||||
if (Arguments::arguments.measure)
|
||||
@ -71,10 +175,6 @@ void MainController::runNoGui(bool detach)
|
||||
pthread_join(this->injectPacketThread, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!detach) {
|
||||
this->deleteInstance();
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::measureCsi(bool stop)
|
||||
@ -82,10 +182,12 @@ void MainController::measureCsi(bool stop)
|
||||
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
||||
if (stop)
|
||||
{
|
||||
this->measuring = false;
|
||||
pthread_cancel(this->measureCsiThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->measuring = true;
|
||||
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
||||
pthread_detach(this->measureCsiThread);
|
||||
}
|
||||
@ -97,10 +199,12 @@ void MainController::injectPackets(bool stop)
|
||||
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
||||
if (stop)
|
||||
{
|
||||
this->injecting = false;
|
||||
pthread_cancel(this->injectPacketThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->injecting = true;
|
||||
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
||||
pthread_detach(this->injectPacketThread);
|
||||
}
|
||||
@ -108,17 +212,23 @@ void MainController::injectPackets(bool stop)
|
||||
|
||||
void MainController::runGui()
|
||||
{
|
||||
this->initInterface();
|
||||
Arguments::arguments.plot = true;
|
||||
Arguments::arguments.verbose = true;
|
||||
Arguments::arguments.measure = false;
|
||||
Arguments::arguments.inject = false;
|
||||
gtk_init(NULL, NULL);
|
||||
Glib::init();
|
||||
auto app = Gtk::Application::create("com.kuskosoft.feitcsi");
|
||||
this->plotAmplitude = new Plot();
|
||||
this->plotPhase = new Plot();
|
||||
this->app = Gtk::Application::create("com.kuskosoft.feitcsi");
|
||||
std::string layout((char*)MainWindow_glade);
|
||||
//auto builder = Gtk::Builder::create_from_file("MainWindow.glade");
|
||||
auto builder = Gtk::Builder::create_from_string(layout);
|
||||
builder->get_widget_derived("MainWindow", this->mainWindow);
|
||||
app->run(*this->mainWindow);
|
||||
this->initPlots();
|
||||
//g_signal_connect(app, "delete-event", G_CALLBACK(MainController::deleteInstance), NULL);
|
||||
this->app->run(*this->mainWindow);
|
||||
}
|
||||
|
||||
void MainController::runUdpSocket()
|
||||
@ -153,7 +263,7 @@ void MainController::initInterface()
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500)); //wait on init then set power and go next
|
||||
this->wifiController.setTxPower();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
@ -226,7 +336,7 @@ void *MainController::injectPackets(void *arg)
|
||||
Logger::log(error) << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -262,15 +372,19 @@ void MainController::restoreState()
|
||||
}
|
||||
|
||||
MainController::~MainController()
|
||||
{
|
||||
{
|
||||
this->restoreState();
|
||||
if (udpSocket) {
|
||||
delete udpSocket;
|
||||
}
|
||||
if (csiToPlot)
|
||||
{
|
||||
delete csiToPlot;
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::intHandler(int dummy)
|
||||
{
|
||||
delete MainController::INSTANCE;
|
||||
MainController::INSTANCE->deleteInstance();
|
||||
exit(0);
|
||||
}
|
||||
@ -18,7 +18,6 @@
|
||||
|
||||
#include "WiFiCsiController.h"
|
||||
#include "Csi.h"
|
||||
#include "GnuPlot.h"
|
||||
#include "MainController.h"
|
||||
#include "Arguments.h"
|
||||
|
||||
@ -44,8 +43,6 @@ void WiFiCsiController::init()
|
||||
|
||||
int WiFiCsiController::listenToCsi()
|
||||
{
|
||||
gnuPlot.init();
|
||||
|
||||
Cmd cmd{
|
||||
.id = NL80211_CMD_VENDOR,
|
||||
.idby = CIB_NETDEV,
|
||||
@ -69,7 +66,6 @@ nla_put_failure:
|
||||
|
||||
int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
WiFiCsiController *instance = (WiFiCsiController *)arg;
|
||||
struct nlattr *attrs[MAX_CMD + 1];
|
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||
|
||||
@ -121,7 +117,13 @@ int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg)
|
||||
} else {
|
||||
c->save();
|
||||
}
|
||||
instance->gnuPlot.updateChartAsync(c); // also delete c
|
||||
if (Arguments::arguments.plot)
|
||||
{
|
||||
WiFiCsiController::csiQueueMutex.lock();
|
||||
WiFiCsiController::csiQueue.push(c);
|
||||
WiFiCsiController::csiQueueMutex.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
#include "gui/CsiProcessingWindow.h"
|
||||
#include "GnuPlot.h"
|
||||
#include "main.h"
|
||||
#include "Logger.h"
|
||||
#include "Arguments.h"
|
||||
@ -65,10 +64,10 @@ void CsiProcessingWindow::refresh()
|
||||
|
||||
this->currentDataCount->set_text(indexLabelText);
|
||||
|
||||
if (!this->csiProcessor.csiData.empty())
|
||||
/* if (!this->csiProcessor.csiData.empty())
|
||||
{
|
||||
gnuPlot.updateChartAsync(this->csiProcessor.csiData[this->currentIndex]);
|
||||
}
|
||||
} */
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,155 +0,0 @@
|
||||
/*
|
||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||
*
|
||||
* 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 "gui/GnuPlot.h"
|
||||
#include "Arguments.h"
|
||||
#include "GnuPlot.h"
|
||||
|
||||
void GnuPlot::init()
|
||||
{
|
||||
if (!Arguments::arguments.plot || gnuPlotPipe)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->gnuPlotPipe = popen("gnuplot > NUL 2>&1", "w");
|
||||
fprintf(this->gnuPlotPipe, "set term x11 noraise\n");
|
||||
fflush(this->gnuPlotPipe);
|
||||
}
|
||||
|
||||
void GnuPlot::setWindow(uint64_t id)
|
||||
{
|
||||
this->gnuPlotPipe = popen("gnuplot > NUL 2>&1", "w");
|
||||
fprintf(this->gnuPlotPipe, "set term x11 noraise\n");
|
||||
fprintf(this->gnuPlotPipe, "set terminal x11 window \"%#lx\"\n", id);
|
||||
fflush(this->gnuPlotPipe);
|
||||
}
|
||||
|
||||
void GnuPlot::setBlank()
|
||||
{
|
||||
const char *cmd = R"(
|
||||
set multiplot layout 2,1
|
||||
set ylabel "Magnitude"
|
||||
set xlabel "Subcarrier Index"
|
||||
set title "Magnitude (Abs) "
|
||||
set autoscale x
|
||||
set xrange [0:1]
|
||||
set yrange [0:1]
|
||||
plot 1/0
|
||||
set ylabel "Phase (rad) "
|
||||
set xlabel "Subcarrier Index"
|
||||
set title "Phase "
|
||||
set autoscale
|
||||
set xrange [0:1]
|
||||
set yrange [0:1]
|
||||
plot 1/0
|
||||
)";
|
||||
this->lastCmd = cmd;
|
||||
fprintf(this->gnuPlotPipe, cmd);
|
||||
fflush(this->gnuPlotPipe);
|
||||
}
|
||||
|
||||
void GnuPlot::updateChartAsync(Csi *csi)
|
||||
{
|
||||
if (!Arguments::arguments.plot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isEventRunning = false;
|
||||
|
||||
if (this->runningAsyncEvent.valid())
|
||||
{
|
||||
isEventRunning = this->runningAsyncEvent.wait_for(std::chrono::seconds(0)) != std::future_status::ready;
|
||||
}
|
||||
|
||||
if (!isEventRunning)
|
||||
{
|
||||
std::future<void> tmpEvent = std::async(&GnuPlot::updateChart, this, csi);
|
||||
this->runningAsyncEvent = std::move(tmpEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void GnuPlot::updateChart(Csi *csi)
|
||||
{
|
||||
std::stringstream ss;
|
||||
// ss << "set multiplot layout 2,1";
|
||||
ss << R"(
|
||||
set multiplot layout 2,1
|
||||
set ylabel "Magnitude"
|
||||
set xlabel "Subcarrier Index"
|
||||
set title "Magnitude (Abs) "
|
||||
set autoscale x
|
||||
)";
|
||||
|
||||
std::stringstream plotCmd;
|
||||
plotCmd << "set xrange [1:" << csi->numSubCarriers << "]\n";
|
||||
plotCmd << "plot";
|
||||
for (uint32_t tx = 0; tx < csi->numTx; tx++)
|
||||
{
|
||||
for (uint32_t rx = 0; rx < csi->numRx; rx++)
|
||||
{
|
||||
plotCmd << " '-' with lines title \"RX" << (rx + 1) << "TX" << (tx + 1) << "\", ";
|
||||
}
|
||||
}
|
||||
plotCmd << "\n";
|
||||
ss << plotCmd.str();
|
||||
|
||||
std::stringstream dataMagnitude;
|
||||
std::stringstream dataPhase;
|
||||
|
||||
uint32_t index = 0;
|
||||
for (uint32_t rx = 0; rx < csi->numRx; rx++)
|
||||
{
|
||||
for (uint32_t tx = 0; tx < csi->numTx; tx++)
|
||||
{
|
||||
for (uint32_t n = 0; n < csi->numSubCarriers; n++)
|
||||
{
|
||||
dataMagnitude << (n + 1) << " " << csi->magnitude[index] << "\n";
|
||||
dataPhase << (n + 1) << " " << csi->phase[index] << "\n";
|
||||
index++;
|
||||
}
|
||||
dataMagnitude << "e\n";
|
||||
dataPhase << "e\n";
|
||||
}
|
||||
}
|
||||
|
||||
ss << dataMagnitude.str();
|
||||
|
||||
ss << R"(
|
||||
set ylabel "Phase (rad) "
|
||||
set xlabel "Subcarrier Index"
|
||||
set title "Phase "
|
||||
set autoscale
|
||||
)";
|
||||
|
||||
ss << plotCmd.str();
|
||||
ss << dataPhase.str();
|
||||
ss << "unset multiplot\n";
|
||||
|
||||
this->lastCmd = ss.str();
|
||||
fprintf(this->gnuPlotPipe, ss.str().c_str());
|
||||
fflush(this->gnuPlotPipe);
|
||||
delete csi;
|
||||
}
|
||||
|
||||
void GnuPlot::reload()
|
||||
{
|
||||
fprintf(this->gnuPlotPipe, this->lastCmd.c_str());
|
||||
fflush(this->gnuPlotPipe);
|
||||
}
|
||||
@ -29,6 +29,16 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
||||
set_title("FeitCSI");
|
||||
set_default_size(1000, 800);
|
||||
|
||||
this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::onWindowDelete));
|
||||
|
||||
if (!Arguments::arguments.gui && Arguments::arguments.plot)
|
||||
{
|
||||
Glib::RefPtr<Gtk::ScrolledWindow>::cast_dynamic(this->builder->get_object("consoleScrollbar"))->hide();
|
||||
Glib::RefPtr<Gtk::Notebook>::cast_dynamic(this->builder->get_object("leftBar"))->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this->measureButton = Glib::RefPtr<Gtk::Button>::cast_dynamic(this->builder->get_object("measureButton"));
|
||||
this->measureButton->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::measureButtonClicked));
|
||||
|
||||
@ -80,8 +90,6 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
||||
|
||||
this->errorMessages = Glib::RefPtr<Gtk::Label>::cast_dynamic(this->builder->get_object("errorMessages"));
|
||||
|
||||
this->chartSocket = Glib::RefPtr<Gtk::Socket>::cast_dynamic(this->builder->get_object("chartSocket"));
|
||||
|
||||
this->console = Glib::RefPtr<Gtk::TextView>::cast_dynamic(this->builder->get_object("console"));
|
||||
Glib::RefPtr<Gtk::ScrolledWindow> consoleScrollbar = Glib::RefPtr<Gtk::ScrolledWindow>::cast_dynamic(this->builder->get_object("consoleScrollbar"));
|
||||
this->consoleScrollbarAdjustment = consoleScrollbar->get_vadjustment();
|
||||
@ -92,12 +100,9 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
||||
|
||||
Logger::guiOutput = this->console->get_buffer();
|
||||
|
||||
this->gnuPlot.setWindow(this->chartSocket->get_id());
|
||||
|
||||
this->signal_check_resize().connect_notify(sigc::mem_fun(*this, &MainWindow::onResize));
|
||||
|
||||
this->signal_show().connect(sigc::mem_fun(*this, &MainWindow::onWindowInit));
|
||||
this->signal_delete_event().connect(sigc::mem_fun(*this, &MainWindow::onWindowDelete));
|
||||
|
||||
this->csiProcessingWindow.init(this->builder);
|
||||
}
|
||||
@ -118,16 +123,14 @@ bool MainWindow::onWindowDelete(GdkEventAny *event)
|
||||
{
|
||||
MainController *mainController = MainController::getInstance();
|
||||
mainController->deleteInstance();
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::onWindowInit()
|
||||
{
|
||||
Glib::RefPtr<Gtk::Paned> socketConsolePaned = Glib::RefPtr<Gtk::Paned>::cast_dynamic(this->builder->get_object("socketConsolePaned"));
|
||||
socketConsolePaned->set_position(socketConsolePaned->get_allocated_height() * 0.8);
|
||||
MainController *mainController = MainController::getInstance();
|
||||
mainController->initInterface();
|
||||
this->gnuPlot.setBlank();
|
||||
}
|
||||
|
||||
void MainWindow::onResize()
|
||||
@ -137,21 +140,6 @@ void MainWindow::onResize()
|
||||
this->consoleScrollbarAdjustment->set_value(this->consoleScrollbarAdjustment->get_upper());
|
||||
return;
|
||||
}
|
||||
|
||||
int pHeight = this->chartSocket->get_allocated_height();
|
||||
int pWidth = this->chartSocket->get_allocated_width();
|
||||
|
||||
if (pHeight < 10 && pWidth < 10)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pWidth != this->chartWidth || pHeight != this->chartHeight)
|
||||
{
|
||||
this->gnuPlot.reload();
|
||||
this->chartWidth = pWidth;
|
||||
this->chartHeight = pHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::frequencyChange()
|
||||
|
||||
175
src/gui/Plot.cpp
Normal file
175
src/gui/Plot.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||
* Copyright (C) 2024 Miroslav Hutar.
|
||||
*
|
||||
* 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 "Plot.h"
|
||||
#include "Arguments.h"
|
||||
#include "Logger.h"
|
||||
#include "WiFiCsiController.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
void Plot::init()
|
||||
{
|
||||
signal_draw().connect(sigc::mem_fun(*this, &Plot::on_draw));
|
||||
}
|
||||
|
||||
void Plot::init(Glib::RefPtr<Gtk::Box> box)
|
||||
{
|
||||
this->yTicks = this->yTicksMin >= 0 ? this->yTicksMax : this->yTicksMax + (this->yTicksMin * -1);
|
||||
signal_draw().connect(sigc::mem_fun(*this, &Plot::on_draw));
|
||||
box->pack_start(*this);
|
||||
show();
|
||||
}
|
||||
|
||||
void Plot::updateData(Csi *csi, std::vector<double> *data)
|
||||
{
|
||||
this->csi = csi;
|
||||
this->data = data;
|
||||
queue_draw();
|
||||
}
|
||||
|
||||
bool Plot::on_draw(const Cairo::RefPtr<Cairo::Context> &cr)
|
||||
{
|
||||
Gtk::Allocation allocation = get_allocation();
|
||||
const int width = allocation.get_width();
|
||||
const int height = allocation.get_height();
|
||||
const double offset = 50;
|
||||
const double xTicks = csi ? csi->numSubCarriers : 56;
|
||||
bool redraw = false;
|
||||
|
||||
// Set background color
|
||||
cr->set_source_rgb(1.0, 1.0, 1.0); // White
|
||||
cr->paint();
|
||||
|
||||
// Draw x and y axis
|
||||
cr->set_source_rgb(0.0, 0.0, 0.0); // Black
|
||||
cr->set_line_width(1.0);
|
||||
cr->move_to(offset, offset);
|
||||
cr->line_to(offset, height - offset);
|
||||
cr->line_to(width - offset, height - offset);
|
||||
cr->stroke();
|
||||
|
||||
// Calculate the scaling factor for data points to fit in the drawing area
|
||||
double xScale = (width - 2 * offset) / (xTicks - 1);
|
||||
double yScale = (height - 2 * offset) / yTicks;
|
||||
|
||||
// Draw tick text
|
||||
const int perXTick = xTicks / 8;
|
||||
for (int i = 0; i < xTicks; i = i + perXTick)
|
||||
{
|
||||
double x = i * xScale + offset;
|
||||
cr->set_source_rgb(0, 0, 0);
|
||||
cr->move_to(x, height - 40);
|
||||
cr->show_text(std::to_string((i + 1)));
|
||||
// Draw grid lines
|
||||
cr->set_source_rgb(0.8, 0.7, 0.7); // Light gray
|
||||
cr->move_to(x, offset);
|
||||
cr->line_to(x, height - offset);
|
||||
cr->stroke();
|
||||
}
|
||||
const int perYTick = yTicks > 8 ? yTicks / 8 : 1;
|
||||
for (int i = 0; i <= yTicks; i = i + perYTick)
|
||||
{
|
||||
double y = height - offset - i * (height - 100) / yTicks;
|
||||
cr->set_source_rgb(0, 0, 0);
|
||||
cr->move_to(30, y);
|
||||
cr->show_text(std::to_string((int)(this->yTicksMin < 0 ? i + this->yTicksMin : i)));
|
||||
// Draw grid lines
|
||||
cr->set_source_rgb(0.8, 0.8, 0.8); // Light gray
|
||||
cr->move_to(offset, y);
|
||||
cr->line_to(width - offset, y);
|
||||
cr->stroke();
|
||||
}
|
||||
|
||||
if (csi)
|
||||
{
|
||||
// Draw the line chart
|
||||
int colorNumber = 0;
|
||||
uint32_t index = 0;
|
||||
for (uint32_t rx = 0; rx < csi->numRx; rx++)
|
||||
{
|
||||
for (uint32_t tx = 0; tx < csi->numTx; tx++)
|
||||
{
|
||||
cr->set_source_rgb(colors[colorNumber][0], colors[colorNumber][1], colors[colorNumber][2]); // Black color
|
||||
cr->set_line_width(2.0);
|
||||
cr->move_to(offset, (height - offset) - (this->yTicksMin < 0 ? ((this->yTicksMin * -1) + (*this->data)[0]) : (*this->data)[0]) * yScale);
|
||||
for (uint32_t n = 0; n < csi->numSubCarriers; n++)
|
||||
{
|
||||
double x = n * xScale + offset;
|
||||
double y = (height - offset) - (this->yTicksMin < 0 ? ((this->yTicksMin * -1) + (*this->data)[index]) : (*this->data)[index]) * yScale;
|
||||
if ((*this->data)[index] > this->yTicks)
|
||||
{
|
||||
this->yTicks = (*this->data)[index];
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
cr->line_to(x, y);
|
||||
index++;
|
||||
}
|
||||
cr->stroke();
|
||||
colorNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
{
|
||||
return this->on_draw(cr);
|
||||
}
|
||||
|
||||
|
||||
// Draw legend
|
||||
colorNumber = 0;
|
||||
cr->move_to(offset * 2, offset - 10);
|
||||
for (uint32_t rx = 0; rx < csi->numRx; rx++)
|
||||
{
|
||||
for (uint32_t tx = 0; tx < csi->numTx; tx++)
|
||||
{
|
||||
cr->set_source_rgb(colors[colorNumber][0], colors[colorNumber][1], colors[colorNumber][2]); // Black color
|
||||
cr->show_text(std::string("RX" + std::to_string(rx + 1)) + std::string("TX" + std::to_string(tx + 1)));
|
||||
cr->show_text(" ");
|
||||
colorNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw x label
|
||||
cr->set_source_rgb(0.0, 0.0, 0.0); // Black color
|
||||
cr->move_to(width / 2, (offset / 2));
|
||||
cr->show_text(this->title);
|
||||
|
||||
// Draw x label
|
||||
cr->set_source_rgb(0.0, 0.0, 0.0); // Black color
|
||||
cr->move_to(width / 2, height - (offset / 2));
|
||||
cr->show_text(this->xLabel);
|
||||
|
||||
// Draw y label
|
||||
cr->move_to((offset / 2), height / 2);
|
||||
cr->rotate(-M_PI / 2); // Rotate text for y label
|
||||
cr->show_text(this->yLabel);
|
||||
|
||||
// Draw x label
|
||||
|
||||
cr->set_source_rgb(0.0, 0.0, 0.0); // Black color
|
||||
cr->set_font_size(30);
|
||||
cr->move_to(width / 2, (offset / 2));
|
||||
cr->show_text(this->title);
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user