mirror of
https://github.com/KuskoSoft/FeitCSI.git
synced 2026-08-27 22:28:34 +03:00
Compare commits
No commits in common. "master" and "v1.1.0" have entirely different histories.
@ -87,7 +87,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkNotebook" id="leftBar">
|
<object class="GtkNotebook">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<child>
|
<child>
|
||||||
@ -819,16 +819,9 @@
|
|||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="plotBox">
|
<object class="GtkSocket" id="chartSocket">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="resize">False</property>
|
<property name="resize">False</property>
|
||||||
|
|||||||
2
Makefile
2
Makefile
@ -24,7 +24,7 @@ INCLUDE_LIB_DIRS =
|
|||||||
INCLUDE_LIB = $(foreach includedir,$(INCLUDE_LIB_DIRS),-L$(includedir))
|
INCLUDE_LIB = $(foreach includedir,$(INCLUDE_LIB_DIRS),-L$(includedir))
|
||||||
|
|
||||||
# Set compiler, preprocesor and linker flags
|
# Set compiler, preprocesor and linker flags
|
||||||
CXXFLAGS += -g -O1 -Wall -std=c++17 $(CDEFS) $(INCLUDE)
|
CXXFLAGS += -g -O3 -Wall -std=c++17 $(CDEFS) $(INCLUDE)
|
||||||
CPPFLAGS += `pkg-config --cflags gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap`
|
CPPFLAGS += `pkg-config --cflags gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap`
|
||||||
LDFLAGS += $(INCLUDE_LIB)
|
LDFLAGS += $(INCLUDE_LIB)
|
||||||
LDLIBS += `pkg-config --libs gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap`
|
LDLIBS += `pkg-config --libs gtkmm-3.0 libnl-3.0 libnl-genl-3.0 libpcap`
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2024-2025 Miroslav Hutar.
|
* Copyright (C) 2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -25,11 +25,8 @@
|
|||||||
#include <argp.h>
|
#include <argp.h>
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
#define ETH_ALEN 6
|
|
||||||
|
|
||||||
struct Args
|
struct Args
|
||||||
{
|
{
|
||||||
bool strict;
|
|
||||||
bool verbose;
|
bool verbose;
|
||||||
uint16_t frequency;
|
uint16_t frequency;
|
||||||
bool gui = false;
|
bool gui = false;
|
||||||
@ -51,16 +48,6 @@ struct Args
|
|||||||
bool measure;
|
bool measure;
|
||||||
std::string mode;
|
std::string mode;
|
||||||
std::string ltf;
|
std::string ltf;
|
||||||
uint32_t modeDelay;
|
|
||||||
bool ftm = false;
|
|
||||||
bool ftmResponder = false;
|
|
||||||
bool ftmAsap = false;
|
|
||||||
uint8_t ftmBurstExp;
|
|
||||||
uint8_t ftmPerBurst;
|
|
||||||
uint16_t ftmBurstPeriod;
|
|
||||||
uint8_t ftmBurstDuration;
|
|
||||||
uint8_t mac[ETH_ALEN];
|
|
||||||
uint8_t ftmTargetMac[ETH_ALEN];
|
|
||||||
std::string inputFile;
|
std::string inputFile;
|
||||||
std::map<enum processor, bool> processors;
|
std::map<enum processor, bool> processors;
|
||||||
};
|
};
|
||||||
@ -98,22 +85,13 @@ private:
|
|||||||
{"coding", 'c', "CODING", 0, "Coding scheme [LDPC|BCC]"},
|
{"coding", 'c', "CODING", 0, "Coding scheme [LDPC|BCC]"},
|
||||||
{"tx-power", 't', "TXPOWER", 0, "TX power of antenna in dBm [1-22]"},
|
{"tx-power", 't', "TXPOWER", 0, "TX power of antenna in dBm [1-22]"},
|
||||||
{"antenna", 'a', "ANTENNA", 0, "Transmitting antenna 1, 2 or 12 for both"},
|
{"antenna", 'a', "ANTENNA", 0, "Transmitting antenna 1, 2 or 12 for both"},
|
||||||
{"mode", 'i', "MODE", 0, "Mode of program[measure|inject|measureinject|ftm|ftmres|injectftmres|measureftm]"},
|
{"mode", 'i', "MODE", 0, "Mode of program[measure|inject|measureinject]"},
|
||||||
{"inject-delay", 'd', "INJECTDELAY", 0, "Delay between frame injections is us"},
|
{"inject-delay", 'd', "INJECTDELAY", 0, "Delay between frame injections is us"},
|
||||||
{"inject-repeat", 'j', "INJECTREPEAT", 0, "How many times inject frame"},
|
{"inject-repeat", 'j', "INJECTREPEAT", 0, "How many times inject frame"},
|
||||||
{"verbose", 'v', 0, OPTION_ARG_OPTIONAL, "Produce verbose output"},
|
{"verbose", 'v', 0, OPTION_ARG_OPTIONAL, "Produce verbose output"},
|
||||||
{"plot", 'p', 0, OPTION_ARG_OPTIONAL, "Plot CSI data"},
|
{"plot", 'p', 0, OPTION_ARG_OPTIONAL, "Plot CSI data"},
|
||||||
{"gui", 'x', 0, OPTION_ARG_OPTIONAL, "Run application in GUI"},
|
{"gui", 'x', 0, OPTION_ARG_OPTIONAL, "Run application in GUI"},
|
||||||
{"udp-socket", 'u', 0, OPTION_ARG_OPTIONAL, "Run application and listen to UDP"},
|
{"udp-socket", 'u', 0, OPTION_ARG_OPTIONAL, "Run application and listen to UDP"},
|
||||||
{"ftm-asap", 'b', 0, OPTION_ARG_OPTIONAL, "FTM asap mode"},
|
|
||||||
{"ftm-burst-exp", 'q', "FTMBURSTEXP", 0, "FTM burst exponent"},
|
|
||||||
{"ftm-per-burst", 'e', "FTMPERBURST", 0, "FTM samples per burst"},
|
|
||||||
{"ftm-burst-period", 'h', "FTMBURSTPERIOD", 0, "FTM burst period"},
|
|
||||||
{"ftm-burst-duration", 'k', "FTMBURTSDURATION", 0, "FTM burst duration"},
|
|
||||||
{"ftm-mac", 'n', "FTMMAC", 0, "FTM target MAC address xx:xx:xx:xx:xx:xx"},
|
|
||||||
{"mode-delay", 'y', "SWAPTIME", 0, "Delay in ms between inject and ftm responder or measure and ftm initiator when modes are injectftmres|measureftm"},
|
|
||||||
{"strict", 'z', 0, OPTION_ARG_OPTIONAL, "Strict mode: filter out values that do not contain a specific MCS"},
|
|
||||||
{"mac", '#', "MAC", 0, "Default NICs MAC will be change to providing MAC xx:xx:xx:xx:xx:xx"},
|
|
||||||
{0}};
|
{0}};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -27,14 +27,12 @@
|
|||||||
|
|
||||||
#define CSI_HEADER_LENGTH 272
|
#define CSI_HEADER_LENGTH 272
|
||||||
|
|
||||||
struct __attribute__((__packed__)) RawHeaderData
|
struct RawHeaderData
|
||||||
{
|
{
|
||||||
uint32_t csiDataSize;
|
uint32_t csiDataSize;
|
||||||
uint32_t space4;
|
uint32_t space4;
|
||||||
uint32_t ftmClock;
|
uint32_t ftmClock;
|
||||||
//uint8_t space12[34];
|
uint8_t space12[34];
|
||||||
uint64_t timestamp;
|
|
||||||
uint8_t space20[26];
|
|
||||||
uint8_t numRx;
|
uint8_t numRx;
|
||||||
uint8_t numTx;
|
uint8_t numTx;
|
||||||
uint8_t space48[4];
|
uint8_t space48[4];
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -23,8 +23,6 @@
|
|||||||
#include "WiFIController.h"
|
#include "WiFIController.h"
|
||||||
#include "PacketInjector.h"
|
#include "PacketInjector.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/Plot.h"
|
|
||||||
#include "Csi.h"
|
|
||||||
#include "UdpSocket.h"
|
#include "UdpSocket.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -34,16 +32,8 @@ class MainController
|
|||||||
public:
|
public:
|
||||||
inline static UdpSocket *udpSocket = nullptr;
|
inline static UdpSocket *udpSocket = nullptr;
|
||||||
|
|
||||||
Plot *plotAmplitude;
|
|
||||||
|
|
||||||
Plot *plotPhase;
|
|
||||||
|
|
||||||
static MainController *getInstance();
|
static MainController *getInstance();
|
||||||
|
|
||||||
static gint updatePlots();
|
|
||||||
|
|
||||||
void initPlots();
|
|
||||||
|
|
||||||
static void deleteInstance();
|
static void deleteInstance();
|
||||||
|
|
||||||
void runNoGui(bool detach = false);
|
void runNoGui(bool detach = false);
|
||||||
@ -69,20 +59,10 @@ private:
|
|||||||
|
|
||||||
inline static MainWindow *mainWindow = nullptr;
|
inline static MainWindow *mainWindow = nullptr;
|
||||||
|
|
||||||
Glib::RefPtr<Gtk::Application> app;
|
|
||||||
|
|
||||||
inline static Csi *csiToPlot = nullptr;
|
|
||||||
|
|
||||||
guint updatePlotsSourceId = 0;
|
|
||||||
|
|
||||||
pthread_t measureCsiThread = 0;
|
pthread_t measureCsiThread = 0;
|
||||||
|
|
||||||
pthread_t injectPacketThread = 0;
|
pthread_t injectPacketThread = 0;
|
||||||
|
|
||||||
pthread_t ftmThread = 0;
|
|
||||||
|
|
||||||
pthread_t ftmResponderThread = 0;
|
|
||||||
|
|
||||||
PacketInjector packetInjector;
|
PacketInjector packetInjector;
|
||||||
|
|
||||||
WiFIController wifiController;
|
WiFIController wifiController;
|
||||||
@ -95,17 +75,6 @@ private:
|
|||||||
|
|
||||||
static void *injectPackets(void *arg);
|
static void *injectPackets(void *arg);
|
||||||
|
|
||||||
static void *ftm(void *arg);
|
|
||||||
|
|
||||||
static void *ftmResponder(void *arg);
|
|
||||||
|
|
||||||
bool measuring = false;
|
|
||||||
|
|
||||||
bool injecting = false;
|
|
||||||
|
|
||||||
bool ftmEnabled = false;
|
|
||||||
|
|
||||||
bool ftmResponderEnabled = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -36,6 +36,7 @@ public:
|
|||||||
void injectVHT();
|
void injectVHT();
|
||||||
void injectHE();
|
void injectHE();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void send(uint32_t rateNFlags);
|
void send(uint32_t rateNFlags);
|
||||||
pcap_t *ppcap = nullptr;
|
pcap_t *ppcap = nullptr;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -64,36 +64,28 @@ class WiFIController : public Netlink
|
|||||||
public:
|
public:
|
||||||
void killNetworkProcesses();
|
void killNetworkProcesses();
|
||||||
void getInterfaces();
|
void getInterfaces();
|
||||||
void getInterfaceInfo(const char* ifname);
|
|
||||||
void setTxPower();
|
void setTxPower();
|
||||||
void addDevice(const char *name, const nl80211_iftype type);
|
void addMonitorDevice(const char *name, const nl80211_iftype type);
|
||||||
int setInterfaceUpDown(const char *ifName, bool up);
|
int setInterfaceUpDown(const char *ifName, bool up);
|
||||||
void setFreq(uint16_t freq, const char *bw);
|
void setFreq(uint16_t freq, const char *bw);
|
||||||
void removeInterface(const char *name, int ifIndex = 0);
|
void removeInterface(const char *name, int ifIndex = 0);
|
||||||
void createMonitorInteface();
|
|
||||||
void createApInteface();
|
|
||||||
void getPhys();
|
void getPhys();
|
||||||
int setApMode();
|
|
||||||
static ChanMode getChanMode(const char *width);
|
static ChanMode getChanMode(const char *width);
|
||||||
static uint16_t chanModeToWidth(ChanMode &chanMode);
|
static uint16_t chanModeToWidth(ChanMode &chanMode);
|
||||||
std::vector<InterfaceInfo> interfaces;
|
std::vector<InterfaceInfo> interfaces;
|
||||||
std::vector<int64_t> phys;
|
std::vector<int64_t> phys;
|
||||||
InterfaceInfo currentInterfaceInfo = {};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string macN2a(const unsigned char *arg);
|
std::string macN2a(const unsigned char *arg);
|
||||||
int freqToCHannel(int freq);
|
int freqToCHannel(int freq);
|
||||||
int phyLookup(char *name);
|
int phyLookup(char *name);
|
||||||
std::string currentDeviceName;
|
|
||||||
|
|
||||||
static int getCf1(const ChanMode *chanmode, unsigned long freq);
|
static int getCf1(const ChanMode *chanmode, unsigned long freq);
|
||||||
static int processGetInterfacesHandler(nl_msg *msg, void *arg);
|
static int processGetInterfacesHandler(nl_msg *msg, void *arg);
|
||||||
static int processGetInterfaceInfoHandler(nl_msg *msg, void *arg);
|
|
||||||
static int processGetPhysHandler(nl_msg *msg, void *arg);
|
static int processGetPhysHandler(nl_msg *msg, void *arg);
|
||||||
static int processSetFreq(nl80211_state *state, nl_msg *msg, void *arg);
|
static int processSetFreq(nl80211_state *state, nl_msg *msg, void *arg);
|
||||||
static int setTxPowerHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
static int setTxPowerHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
||||||
static int processaddDeviceHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
static int processAddMonitorDeviceHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
||||||
static int setApModeHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -20,9 +20,7 @@
|
|||||||
#define WIFI_CSI_CONTROLLER_H
|
#define WIFI_CSI_CONTROLLER_H
|
||||||
|
|
||||||
#include "Netlink.h"
|
#include "Netlink.h"
|
||||||
#include "Csi.h"
|
#include "GnuPlot.h"
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#define IWL_MVM_VENDOR_ATTR_CSI_HDR 0x4d
|
#define IWL_MVM_VENDOR_ATTR_CSI_HDR 0x4d
|
||||||
#define IWL_MVM_VENDOR_ATTR_CSI_DATA 0x4e
|
#define IWL_MVM_VENDOR_ATTR_CSI_DATA 0x4e
|
||||||
@ -34,17 +32,15 @@ class WiFiCsiController : public Netlink
|
|||||||
public:
|
public:
|
||||||
void init();
|
void init();
|
||||||
int listenToCsi();
|
int listenToCsi();
|
||||||
static void enableCsi(bool enable = true);
|
void enableCsi(bool enable = true);
|
||||||
inline static std::mutex csiQueueMutex;
|
|
||||||
inline static std::queue<Csi*> csiQueue;
|
|
||||||
int64_t stopTime = 0;
|
|
||||||
|
|
||||||
~WiFiCsiController();
|
~WiFiCsiController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int listenToCsiHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
static int listenToCsiHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
||||||
static int processListenToCsiHandler(nl_msg *msg, void *arg);
|
static int processListenToCsiHandler(nl_msg *msg, void *arg);
|
||||||
static void printDetail(Csi *c);
|
static void printDetail(Csi &c);
|
||||||
|
GnuPlot gnuPlot;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
|
||||||
* Copyright (C) 2025 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef WIFI_FTM_CONTROLLER_H
|
|
||||||
#define WIFI_FTM_CONTROLLER_H
|
|
||||||
|
|
||||||
#include "Netlink.h"
|
|
||||||
|
|
||||||
struct __attribute__((__packed__)) Ftm
|
|
||||||
{
|
|
||||||
uint32_t burstIndex;
|
|
||||||
uint32_t numFtmrAttempts;
|
|
||||||
uint32_t numFtmrSuccesses;
|
|
||||||
uint32_t numBurstsExp;
|
|
||||||
uint8_t burstDuration;
|
|
||||||
uint8_t ftmsPerBurst;
|
|
||||||
uint8_t rssiAvg;
|
|
||||||
uint32_t rssiSpread;
|
|
||||||
uint64_t rttAvg;
|
|
||||||
uint64_t rttVariance;
|
|
||||||
uint64_t rttSpread;
|
|
||||||
uint64_t distAvg;
|
|
||||||
uint64_t distVariance;
|
|
||||||
uint64_t distSpread;
|
|
||||||
uint64_t timestamp;
|
|
||||||
}; // size 79 bytes
|
|
||||||
|
|
||||||
#define FTM_SIZE sizeof(Ftm)
|
|
||||||
|
|
||||||
class WiFiFtmController : public Netlink
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
void init();
|
|
||||||
int startInitiator();
|
|
||||||
int startResponder();
|
|
||||||
int setApMode();
|
|
||||||
uint64_t lastRttIsSuccess = false;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static int ftmHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
|
||||||
static int ftmResponderHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
|
||||||
static int setApModeHandler(nl80211_state *state, nl_msg *msg, void *arg);
|
|
||||||
static int processFtmHandler(nl_msg *msg, void *arg);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
#include "CsiProcessor.h"
|
#include "CsiProcessor.h"
|
||||||
|
#include "GnuPlot.h"
|
||||||
|
|
||||||
class CsiProcessingWindow
|
class CsiProcessingWindow
|
||||||
{
|
{
|
||||||
@ -54,7 +55,7 @@ private:
|
|||||||
void processingSaveGtkButtonClicked();
|
void processingSaveGtkButtonClicked();
|
||||||
|
|
||||||
CsiProcessor csiProcessor;
|
CsiProcessor csiProcessor;
|
||||||
|
GnuPlot gnuPlot;
|
||||||
uint32_t currentIndex = 0;
|
uint32_t currentIndex = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2024 Miroslav Hutar.
|
* Copyright (C) 2023 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,41 +16,27 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PLOT_H
|
#ifndef GNU_PLOT_H
|
||||||
#define PLOT_H
|
#define GNU_PLOT_H
|
||||||
|
|
||||||
#include "Csi.h"
|
|
||||||
#include <future>
|
|
||||||
#include <sigc++/sigc++.h>
|
|
||||||
#include <string>
|
|
||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
#include <mutex>
|
#include <gtkmm/socket.h>
|
||||||
|
#include <Csi.h>
|
||||||
|
|
||||||
class Plot : public Gtk::DrawingArea
|
#include <string>
|
||||||
|
|
||||||
|
class GnuPlot
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void init();
|
void init();
|
||||||
void init(Glib::RefPtr<Gtk::Box> box);
|
void setWindow(uint64_t id);
|
||||||
void updateData(Csi *csi, std::vector<double> *data);
|
void setBlank();
|
||||||
double yTicksMax = 200;
|
void reload();
|
||||||
double yTicksMin = 0;
|
void updateChart(Csi &csi);
|
||||||
std::string yLabel = "";
|
|
||||||
std::string xLabel = "Subcarrier index";
|
|
||||||
std::string title = "";
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const double colors[4][3] = {
|
inline static std::string lastCmd;
|
||||||
{ 1.0, 0.0, 0.0 },
|
inline static FILE *gnuPlotPipe;
|
||||||
{ 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
|
#endif
|
||||||
@ -20,7 +20,7 @@
|
|||||||
#define MAIN_WINDOW_H
|
#define MAIN_WINDOW_H
|
||||||
|
|
||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
#include "gui/Plot.h"
|
#include "gui/GnuPlot.h"
|
||||||
#include "gui/CsiProcessingWindow.h"
|
#include "gui/CsiProcessingWindow.h"
|
||||||
|
|
||||||
class MainWindow : public Gtk::ApplicationWindow
|
class MainWindow : public Gtk::ApplicationWindow
|
||||||
@ -31,7 +31,6 @@ public:
|
|||||||
MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &builder);
|
MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &builder);
|
||||||
void fatalError(std::string msg);
|
void fatalError(std::string msg);
|
||||||
|
|
||||||
Glib::RefPtr<Gtk::Builder> builder;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int chartWidth = 0;
|
int chartWidth = 0;
|
||||||
@ -45,6 +44,7 @@ private:
|
|||||||
|
|
||||||
std::map<std::string, std::string> errors;
|
std::map<std::string, std::string> errors;
|
||||||
Glib::RefPtr<Gtk::Adjustment> consoleScrollbarAdjustment;
|
Glib::RefPtr<Gtk::Adjustment> consoleScrollbarAdjustment;
|
||||||
|
Glib::RefPtr<Gtk::Builder> builder;
|
||||||
Glib::RefPtr<Gtk::MessageDialog> errorDialog;
|
Glib::RefPtr<Gtk::MessageDialog> errorDialog;
|
||||||
|
|
||||||
Glib::RefPtr<Gtk::Button> measureButton;
|
Glib::RefPtr<Gtk::Button> measureButton;
|
||||||
@ -67,7 +67,8 @@ private:
|
|||||||
Glib::RefPtr<Gtk::TextView> console;
|
Glib::RefPtr<Gtk::TextView> console;
|
||||||
Glib::RefPtr<Gtk::Label> errorMessages;
|
Glib::RefPtr<Gtk::Label> errorMessages;
|
||||||
|
|
||||||
Plot plot;
|
Glib::RefPtr<Gtk::Socket> chartSocket;
|
||||||
|
GnuPlot gnuPlot;
|
||||||
|
|
||||||
void closeButton();
|
void closeButton();
|
||||||
|
|
||||||
|
|||||||
5871
include/layout.h
5871
include/layout.h
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,8 +19,7 @@
|
|||||||
#ifndef MAIN_H
|
#ifndef MAIN_H
|
||||||
#define MAIN_H
|
#define MAIN_H
|
||||||
|
|
||||||
#define MONITOR_INTERFACE_NAME "FeitCSImon"
|
#define MONITOR_INTERFACE_NAME "mon0"
|
||||||
#define AP_INTERFACE_NAME "FeitCSIap"
|
|
||||||
|
|
||||||
enum processor
|
enum processor
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2024-2025 Miroslav Hutar.
|
* Copyright (C) 2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -27,7 +27,6 @@ const char *argp_program_bug_address = "https://github.com/KuskoSoft/FeitCSI/iss
|
|||||||
void Arguments::init()
|
void Arguments::init()
|
||||||
{
|
{
|
||||||
Arguments::arguments = {
|
Arguments::arguments = {
|
||||||
.strict = false,
|
|
||||||
.verbose = false,
|
.verbose = false,
|
||||||
.frequency = 2412,
|
.frequency = 2412,
|
||||||
.gui = false,
|
.gui = false,
|
||||||
@ -43,20 +42,11 @@ void Arguments::init()
|
|||||||
.injectDelay = 100000,
|
.injectDelay = 100000,
|
||||||
.injectRepeat = 0,
|
.injectRepeat = 0,
|
||||||
.coding = "LDPC",
|
.coding = "LDPC",
|
||||||
.format = "HT",
|
.format = "NOHT",
|
||||||
.inject = false,
|
.inject = false,
|
||||||
.measure = true,
|
.measure = true,
|
||||||
.mode = "measure",
|
.mode = "measure",
|
||||||
.ltf = "1xLTF+0.8",
|
.ltf = "1xLTF+0.8",
|
||||||
.modeDelay = 3000,
|
|
||||||
.ftm = false,
|
|
||||||
.ftmResponder = false,
|
|
||||||
.ftmAsap = false,
|
|
||||||
.ftmBurstExp = 0,
|
|
||||||
.ftmPerBurst = 0,
|
|
||||||
.ftmBurstPeriod = 0,
|
|
||||||
.ftmBurstDuration = 0,
|
|
||||||
.mac = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +68,6 @@ error_t Arguments::parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
case 'v':
|
case 'v':
|
||||||
args->verbose = true;
|
args->verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'z':
|
|
||||||
args->strict = true;
|
|
||||||
break;
|
|
||||||
case 'x':
|
case 'x':
|
||||||
args->gui = true;
|
args->gui = true;
|
||||||
break;
|
break;
|
||||||
@ -107,30 +94,9 @@ error_t Arguments::parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
args->measure = true;
|
args->measure = true;
|
||||||
args->inject = true;
|
args->inject = true;
|
||||||
}
|
}
|
||||||
else if (args->mode == "measureftm")
|
|
||||||
{
|
|
||||||
args->measure = true;
|
|
||||||
args->ftm = true;
|
|
||||||
}
|
|
||||||
else if (args->mode == "ftm")
|
|
||||||
{
|
|
||||||
args->measure = false;
|
|
||||||
args->ftm = true;
|
|
||||||
}
|
|
||||||
else if (args->mode == "ftmres")
|
|
||||||
{
|
|
||||||
args->measure = false;
|
|
||||||
args->ftmResponder = true;
|
|
||||||
}
|
|
||||||
else if (args->mode == "injectftmres")
|
|
||||||
{
|
|
||||||
args->measure = false;
|
|
||||||
args->ftmResponder = true;
|
|
||||||
args->inject = true;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
argp_failure(state, 1, 0, "Bad mode. Possible values [measure|inject|measureinject|ftm]");
|
argp_failure(state, 1, 0, "Bad mode. Possible values [measure|inject|measureinject]");
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
exit(ARGP_ERR_UNKNOWN);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -185,17 +151,6 @@ error_t Arguments::parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'y':
|
|
||||||
{
|
|
||||||
int modeDelay = std::atoi(arg);
|
|
||||||
if (modeDelay <= 0)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "Mode delay is not correct number");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
args->modeDelay = (uint32_t)modeDelay;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'g':
|
case 'g':
|
||||||
{
|
{
|
||||||
int gi = std::atoi(arg);
|
int gi = std::atoi(arg);
|
||||||
@ -302,74 +257,6 @@ error_t Arguments::parse_opt(int key, char *arg, struct argp_state *state)
|
|||||||
case 'o':
|
case 'o':
|
||||||
args->outputFile = arg;
|
args->outputFile = arg;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
|
||||||
args->ftmAsap = true;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
{
|
|
||||||
int f = std::atoi(arg);
|
|
||||||
if (f <= 0)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "FTM burst exponent is not correct");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
args->ftmBurstExp = (uint8_t)f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'e':
|
|
||||||
{
|
|
||||||
int f = std::atoi(arg);
|
|
||||||
if (f <= 0)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "FTM per burst is not correct");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
args->ftmPerBurst = (uint8_t)f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'h':
|
|
||||||
{
|
|
||||||
int f = std::atoi(arg);
|
|
||||||
if (f <= 0)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "FTM burst period is not correct");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
args->ftmBurstPeriod = (uint16_t)f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'k':
|
|
||||||
{
|
|
||||||
int f = std::atoi(arg);
|
|
||||||
if (f <= 0)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "FTM burst duration is not correct");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
args->ftmBurstDuration = (uint8_t)f;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '#':
|
|
||||||
{
|
|
||||||
int res = sscanf(arg, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &args->mac[0], &args->mac[1], &args->mac[2], &args->mac[3], &args->mac[4], &args->mac[5]);
|
|
||||||
if (res != ETH_ALEN)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "Bad mac address");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'n':
|
|
||||||
{
|
|
||||||
int res = sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
|
|
||||||
&args->ftmTargetMac[0], &args->ftmTargetMac[1], &args->ftmTargetMac[2], &args->ftmTargetMac[3], &args->ftmTargetMac[4], &args->ftmTargetMac[5]);
|
|
||||||
if (res != ETH_ALEN)
|
|
||||||
{
|
|
||||||
argp_failure(state, 1, 0, "FTM target mac address is not correct");
|
|
||||||
exit(ARGP_ERR_UNKNOWN);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ARGP_KEY_ARG:
|
case ARGP_KEY_ARG:
|
||||||
case ARGP_KEY_END:
|
case ARGP_KEY_END:
|
||||||
if (args->frequency == 0 ||
|
if (args->frequency == 0 ||
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -58,7 +58,6 @@ void Csi::loadFromMemory(uint8_t *pHeader, uint8_t *pRawCsiData)
|
|||||||
memcpy(&this->rawHeaderData, pHeader, CSI_HEADER_LENGTH);
|
memcpy(&this->rawHeaderData, pHeader, CSI_HEADER_LENGTH);
|
||||||
this->rawCsiData = new uint8_t[this->rawHeaderData.csiDataSize];
|
this->rawCsiData = new uint8_t[this->rawHeaderData.csiDataSize];
|
||||||
memcpy(this->rawCsiData, pRawCsiData, this->rawHeaderData.csiDataSize);
|
memcpy(this->rawCsiData, pRawCsiData, this->rawHeaderData.csiDataSize);
|
||||||
//this->rawHeaderData.timestamp = (uint64_t)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
||||||
|
|
||||||
this->processRawCsi();
|
this->processRawCsi();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include "CsiProcessor.h"
|
#include "CsiProcessor.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "GnuPlot.h"
|
||||||
#include "interpolation.h"
|
#include "interpolation.h"
|
||||||
#include "Arguments.h"
|
#include "Arguments.h"
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -21,7 +21,6 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "WiFiFtmController.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -40,137 +39,23 @@ MainController *MainController::getInstance()
|
|||||||
return INSTANCE;
|
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()
|
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;
|
delete MainController::INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainController::runNoGui(bool detach)
|
void MainController::runNoGui(bool detach)
|
||||||
{
|
{
|
||||||
this->initInterface();
|
this->initInterface();
|
||||||
if (Arguments::arguments.plot)
|
if (Arguments::arguments.measure)
|
||||||
{
|
{
|
||||||
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 && !Arguments::arguments.ftm)
|
|
||||||
{
|
|
||||||
this->measureCsi();
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.inject && !Arguments::arguments.ftmResponder)
|
|
||||||
{
|
|
||||||
this->injectPackets();
|
|
||||||
}
|
|
||||||
this->app->run(*this->mainWindow);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Arguments::arguments.measure && !Arguments::arguments.ftm)
|
|
||||||
{
|
|
||||||
this->measuring = true;
|
|
||||||
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
||||||
}
|
}
|
||||||
if (Arguments::arguments.inject && !Arguments::arguments.ftmResponder)
|
if (Arguments::arguments.inject)
|
||||||
{
|
{
|
||||||
this->injecting = true;
|
|
||||||
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
||||||
}
|
}
|
||||||
if (Arguments::arguments.ftm)
|
if (Arguments::arguments.measure)
|
||||||
{
|
|
||||||
this->ftmEnabled = true;
|
|
||||||
pthread_create(&this->ftmThread, NULL, &MainController::ftm, NULL);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmResponder)
|
|
||||||
{
|
|
||||||
this->ftmResponderEnabled = true;
|
|
||||||
pthread_create(&this->ftmResponderThread, NULL, &MainController::ftmResponder, NULL);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.measure && !Arguments::arguments.ftm)
|
|
||||||
{
|
{
|
||||||
if (detach) {
|
if (detach) {
|
||||||
pthread_detach(this->measureCsiThread);
|
pthread_detach(this->measureCsiThread);
|
||||||
@ -178,7 +63,7 @@ void MainController::runNoGui(bool detach)
|
|||||||
pthread_join(this->measureCsiThread, NULL);
|
pthread_join(this->measureCsiThread, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Arguments::arguments.inject && !Arguments::arguments.ftmResponder)
|
if (Arguments::arguments.inject)
|
||||||
{
|
{
|
||||||
if (detach) {
|
if (detach) {
|
||||||
pthread_detach(this->injectPacketThread);
|
pthread_detach(this->injectPacketThread);
|
||||||
@ -186,41 +71,21 @@ void MainController::runNoGui(bool detach)
|
|||||||
pthread_join(this->injectPacketThread, NULL);
|
pthread_join(this->injectPacketThread, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Arguments::arguments.ftm)
|
|
||||||
{
|
if (!detach) {
|
||||||
if (detach)
|
this->deleteInstance();
|
||||||
{
|
|
||||||
pthread_detach(this->ftmThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pthread_join(this->ftmThread, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmResponder)
|
|
||||||
{
|
|
||||||
if (detach)
|
|
||||||
{
|
|
||||||
pthread_detach(this->ftmResponderThread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pthread_join(this->ftmResponderThread, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainController::measureCsi(bool stop)
|
void MainController::measureCsi(bool stop)
|
||||||
{
|
{
|
||||||
|
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
||||||
if (stop)
|
if (stop)
|
||||||
{
|
{
|
||||||
this->measuring = false;
|
|
||||||
pthread_cancel(this->measureCsiThread);
|
pthread_cancel(this->measureCsiThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
|
||||||
this->measuring = true;
|
|
||||||
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
pthread_create(&this->measureCsiThread, NULL, &MainController::measureCsi, NULL);
|
||||||
pthread_detach(this->measureCsiThread);
|
pthread_detach(this->measureCsiThread);
|
||||||
}
|
}
|
||||||
@ -228,15 +93,14 @@ void MainController::measureCsi(bool stop)
|
|||||||
|
|
||||||
void MainController::injectPackets(bool stop)
|
void MainController::injectPackets(bool stop)
|
||||||
{
|
{
|
||||||
|
this->wifiController.setTxPower();
|
||||||
|
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
||||||
if (stop)
|
if (stop)
|
||||||
{
|
{
|
||||||
this->injecting = false;
|
|
||||||
pthread_cancel(this->injectPacketThread);
|
pthread_cancel(this->injectPacketThread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
|
||||||
this->injecting = true;
|
|
||||||
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
pthread_create(&this->injectPacketThread, NULL, &MainController::injectPackets, NULL);
|
||||||
pthread_detach(this->injectPacketThread);
|
pthread_detach(this->injectPacketThread);
|
||||||
}
|
}
|
||||||
@ -244,23 +108,17 @@ void MainController::injectPackets(bool stop)
|
|||||||
|
|
||||||
void MainController::runGui()
|
void MainController::runGui()
|
||||||
{
|
{
|
||||||
this->initInterface();
|
|
||||||
Arguments::arguments.plot = true;
|
Arguments::arguments.plot = true;
|
||||||
Arguments::arguments.verbose = true;
|
Arguments::arguments.verbose = true;
|
||||||
Arguments::arguments.measure = false;
|
Arguments::arguments.measure = false;
|
||||||
Arguments::arguments.inject = false;
|
Arguments::arguments.inject = false;
|
||||||
gtk_init(NULL, NULL);
|
gtk_init(NULL, NULL);
|
||||||
Glib::init();
|
Glib::init();
|
||||||
this->plotAmplitude = new Plot();
|
auto app = Gtk::Application::create("com.kuskosoft.feitcsi");
|
||||||
this->plotPhase = new Plot();
|
|
||||||
this->app = Gtk::Application::create("com.kuskosoft.feitcsi");
|
|
||||||
std::string layout((char*)MainWindow_glade);
|
std::string layout((char*)MainWindow_glade);
|
||||||
//auto builder = Gtk::Builder::create_from_file("MainWindow.glade");
|
|
||||||
auto builder = Gtk::Builder::create_from_string(layout);
|
auto builder = Gtk::Builder::create_from_string(layout);
|
||||||
builder->get_widget_derived("MainWindow", this->mainWindow);
|
builder->get_widget_derived("MainWindow", this->mainWindow);
|
||||||
this->initPlots();
|
app->run(*this->mainWindow);
|
||||||
//g_signal_connect(app, "delete-event", G_CALLBACK(MainController::deleteInstance), NULL);
|
|
||||||
this->app->run(*this->mainWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainController::runUdpSocket()
|
void MainController::runUdpSocket()
|
||||||
@ -287,10 +145,15 @@ void MainController::initInterface()
|
|||||||
this->bkpInterfaces.push_back(interface);
|
this->bkpInterfaces.push_back(interface);
|
||||||
this->wifiController.removeInterface(NULL, interface.ifIndex);
|
this->wifiController.removeInterface(NULL, interface.ifIndex);
|
||||||
}
|
}
|
||||||
this->wifiController.createMonitorInteface();
|
this->wifiController.addMonitorDevice(MONITOR_INTERFACE_NAME, NL80211_IFTYPE_MONITOR);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
this->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, true);
|
||||||
this->wifiController.createApInteface();
|
this->wifiController.setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
if (!MainController::mainWindow)
|
||||||
|
{
|
||||||
|
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)
|
catch(const std::exception& e)
|
||||||
{
|
{
|
||||||
@ -311,8 +174,6 @@ void *MainController::measureCsi(void *arg)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(AP_INTERFACE_NAME, false);
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, true);
|
|
||||||
WiFiCsiController wcs;
|
WiFiCsiController wcs;
|
||||||
wcs.init();
|
wcs.init();
|
||||||
wcs.listenToCsi();
|
wcs.listenToCsi();
|
||||||
@ -332,164 +193,10 @@ void *MainController::measureCsi(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *MainController::ftm(void *arg)
|
|
||||||
{
|
|
||||||
bool firstRun = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(AP_INTERFACE_NAME, true);
|
|
||||||
WiFiFtmController wft;
|
|
||||||
wft.init();
|
|
||||||
if (Arguments::arguments.measure)
|
|
||||||
{
|
|
||||||
uint64_t startFtmTime = 0;
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wft.startInitiator();
|
|
||||||
}
|
|
||||||
catch(const std::exception& e)
|
|
||||||
{
|
|
||||||
std::cerr << e.what() << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (wft.lastRttIsSuccess && firstRun)
|
|
||||||
{
|
|
||||||
firstRun = false;
|
|
||||||
startFtmTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
||||||
if ((startFtmTime + (Arguments::arguments.modeDelay / 2)) > now)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wft.lastRttIsSuccess && !firstRun)
|
|
||||||
{
|
|
||||||
MainController::getInstance()->measureCsi(false);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(Arguments::arguments.modeDelay));
|
|
||||||
MainController::getInstance()->measureCsi(true);
|
|
||||||
WiFiCsiController::enableCsi(false);
|
|
||||||
firstRun = true;
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, false);
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(AP_INTERFACE_NAME, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Arguments::arguments.injectRepeat)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < Arguments::arguments.injectRepeat; i++)
|
|
||||||
{
|
|
||||||
wft.startInitiator();
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
wft.startInitiator();
|
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(Arguments::arguments.injectDelay));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
if (MainController::mainWindow)
|
|
||||||
{
|
|
||||||
MainController::mainWindow->fatalError(e.what());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::log(error) << e.what() << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MainController::ftmResponder(void *arg)
|
|
||||||
{
|
|
||||||
WiFiFtmController wft;
|
|
||||||
wft.init();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Arguments::arguments.inject)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
MainController::getInstance()->injectPackets(false);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(Arguments::arguments.modeDelay));
|
|
||||||
MainController::getInstance()->injectPackets(true);
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, false);
|
|
||||||
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(AP_INTERFACE_NAME, true);
|
|
||||||
MainController::getInstance()->wifiController.getInterfaceInfo(AP_INTERFACE_NAME);
|
|
||||||
while (MainController::getInstance()->wifiController.currentInterfaceInfo.freq == 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wft.startResponder();
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
// Just ignore.. wait on network up
|
|
||||||
}
|
|
||||||
MainController::getInstance()->wifiController.getInterfaceInfo(AP_INTERFACE_NAME);
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(Arguments::arguments.modeDelay));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
while(MainController::getInstance()->wifiController.currentInterfaceInfo.freq == 0) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
wft.startResponder();
|
|
||||||
}
|
|
||||||
catch(const std::exception& e)
|
|
||||||
{
|
|
||||||
//Just ignore.. wait on network up
|
|
||||||
}
|
|
||||||
MainController::getInstance()->wifiController.getInterfaceInfo(AP_INTERFACE_NAME);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.verbose)
|
|
||||||
{
|
|
||||||
Logger::log(info) << "FTM responder was started\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
if (MainController::mainWindow)
|
|
||||||
{
|
|
||||||
MainController::mainWindow->fatalError(e.what());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::log(error) << e.what() << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *MainController::injectPackets(void *arg)
|
void *MainController::injectPackets(void *arg)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(AP_INTERFACE_NAME, false);
|
|
||||||
MainController::getInstance()->wifiController.setInterfaceUpDown(MONITOR_INTERFACE_NAME, true);
|
|
||||||
PacketInjector pi;
|
PacketInjector pi;
|
||||||
if (Arguments::arguments.injectRepeat)
|
if (Arguments::arguments.injectRepeat)
|
||||||
{
|
{
|
||||||
@ -537,14 +244,13 @@ void MainController::restoreState()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mainController->wifiController.removeInterface(MONITOR_INTERFACE_NAME);
|
mainController->wifiController.removeInterface(MONITOR_INTERFACE_NAME);
|
||||||
mainController->wifiController.removeInterface(AP_INTERFACE_NAME);
|
|
||||||
for (InterfaceInfo interface : mainController->bkpInterfaces)
|
for (InterfaceInfo interface : mainController->bkpInterfaces)
|
||||||
{
|
{
|
||||||
if (Arguments::arguments.verbose)
|
if (Arguments::arguments.verbose)
|
||||||
{
|
{
|
||||||
Logger::log(info) << "Recovering interface " << interface.ifName << "\n";
|
Logger::log(info) << "Recovering interface " << interface.ifName << "\n";
|
||||||
}
|
}
|
||||||
mainController->wifiController.addDevice(interface.ifName.c_str(), (nl80211_iftype)interface.ifType);
|
mainController->wifiController.addMonitorDevice(interface.ifName.c_str(), (nl80211_iftype)interface.ifType);
|
||||||
}
|
}
|
||||||
mainController->bkpInterfaces.clear();
|
mainController->bkpInterfaces.clear();
|
||||||
mainController->wifiController.interfaces.clear();
|
mainController->wifiController.interfaces.clear();
|
||||||
@ -561,14 +267,10 @@ MainController::~MainController()
|
|||||||
if (udpSocket) {
|
if (udpSocket) {
|
||||||
delete udpSocket;
|
delete udpSocket;
|
||||||
}
|
}
|
||||||
if (csiToPlot)
|
|
||||||
{
|
|
||||||
delete csiToPlot;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainController::intHandler(int dummy)
|
void MainController::intHandler(int dummy)
|
||||||
{
|
{
|
||||||
MainController::INSTANCE->deleteInstance();
|
delete MainController::INSTANCE;
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -127,7 +127,6 @@ int Netlink::nlExecCommand(Cmd &cmd)
|
|||||||
nl_socket_set_cb(this->nlstate.nl_sock, s_cb);
|
nl_socket_set_cb(this->nlstate.nl_sock, s_cb);
|
||||||
|
|
||||||
err = nl_send_auto_complete(this->nlstate.nl_sock, msg);
|
err = nl_send_auto_complete(this->nlstate.nl_sock, msg);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -146,6 +145,7 @@ int Netlink::nlExecCommand(Cmd &cmd)
|
|||||||
{
|
{
|
||||||
errMsg = "command failed: " + std::string(strerror(-err)) + std::to_string(err) + "\n";
|
errMsg = "command failed: " + std::string(strerror(-err)) + std::to_string(err) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
nl_cb_put(cb);
|
nl_cb_put(cb);
|
||||||
nl_cb_put(s_cb);
|
nl_cb_put(s_cb);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -175,7 +175,7 @@ void PacketInjector::send(uint32_t rateNFlags)
|
|||||||
char szErrbuf[500];
|
char szErrbuf[500];
|
||||||
|
|
||||||
if (ppcap == nullptr) {
|
if (ppcap == nullptr) {
|
||||||
ppcap = pcap_open_live(MONITOR_INTERFACE_NAME, 800, 1, 20, szErrbuf);
|
ppcap = pcap_open_live("mon0", 800, 1, 20, szErrbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = pcap_inject(ppcap, sendBuffer, totalSize);
|
int r = pcap_inject(ppcap, sendBuffer, totalSize);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -107,34 +107,20 @@ void WiFIController::getInterfaces()
|
|||||||
this->nlExecCommand(cmd);
|
this->nlExecCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFIController::getInterfaceInfo(const char *ifname)
|
|
||||||
{
|
|
||||||
Cmd cmd{
|
|
||||||
.id = NL80211_CMD_GET_INTERFACE,
|
|
||||||
.idby = CIB_NETDEV,
|
|
||||||
.nlFlags = 0,
|
|
||||||
.device = if_nametoindex(ifname),
|
|
||||||
.handler = NULL,
|
|
||||||
.valid_handler = this->processGetInterfaceInfoHandler,
|
|
||||||
};
|
|
||||||
|
|
||||||
this->nlExecCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFIController::setTxPower()
|
void WiFIController::setTxPower()
|
||||||
{
|
{
|
||||||
Cmd cmd{
|
Cmd cmd{
|
||||||
.id = NL80211_CMD_SET_WIPHY,
|
.id = NL80211_CMD_SET_WIPHY,
|
||||||
.idby = CIB_NETDEV,
|
.idby = CIB_NETDEV,
|
||||||
.nlFlags = 0,
|
.nlFlags = 0,
|
||||||
.device = if_nametoindex(this->currentDeviceName.c_str()),
|
.device = if_nametoindex(MONITOR_INTERFACE_NAME),
|
||||||
.handler = this->setTxPowerHandler,
|
.handler = this->setTxPowerHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
this->nlExecCommand(cmd);
|
this->nlExecCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFIController::addDevice(const char *name, const nl80211_iftype type)
|
void WiFIController::addMonitorDevice(const char *name, const nl80211_iftype type)
|
||||||
{
|
{
|
||||||
if (this->phys.empty())
|
if (this->phys.empty())
|
||||||
{
|
{
|
||||||
@ -149,7 +135,7 @@ void WiFIController::addDevice(const char *name, const nl80211_iftype type)
|
|||||||
.idby = CIB_PHY,
|
.idby = CIB_PHY,
|
||||||
.nlFlags = 0,
|
.nlFlags = 0,
|
||||||
.device = this->phys[0],
|
.device = this->phys[0],
|
||||||
.handler = this->processaddDeviceHandler,
|
.handler = this->processAddMonitorDeviceHandler,
|
||||||
.valid_handler = NULL,
|
.valid_handler = NULL,
|
||||||
.args = settings,
|
.args = settings,
|
||||||
};
|
};
|
||||||
@ -180,7 +166,6 @@ int WiFIController::setInterfaceUpDown(const char *ifName, bool up)
|
|||||||
if (up)
|
if (up)
|
||||||
{
|
{
|
||||||
ifr.ifr_flags |= IFF_UP;
|
ifr.ifr_flags |= IFF_UP;
|
||||||
this->currentDeviceName = ifName;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -211,7 +196,7 @@ void WiFIController::setFreq(uint16_t freq, const char *bw)
|
|||||||
.id = NL80211_CMD_SET_WIPHY,
|
.id = NL80211_CMD_SET_WIPHY,
|
||||||
.idby = CIB_NETDEV,
|
.idby = CIB_NETDEV,
|
||||||
.nlFlags = 0,
|
.nlFlags = 0,
|
||||||
.device = if_nametoindex(this->currentDeviceName.c_str()),
|
.device = if_nametoindex(MONITOR_INTERFACE_NAME),
|
||||||
.handler = processSetFreq,
|
.handler = processSetFreq,
|
||||||
.valid_handler = NULL,
|
.valid_handler = NULL,
|
||||||
.args = settings,
|
.args = settings,
|
||||||
@ -232,48 +217,6 @@ void WiFIController::removeInterface(const char *name, int ifIndex)
|
|||||||
this->nlExecCommand(cmd);
|
this->nlExecCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFIController::createMonitorInteface()
|
|
||||||
{
|
|
||||||
this->addDevice(MONITOR_INTERFACE_NAME, NL80211_IFTYPE_MONITOR);
|
|
||||||
while (this->currentInterfaceInfo.freq != Arguments::arguments.frequency)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this->setInterfaceUpDown(MONITOR_INTERFACE_NAME, true);
|
|
||||||
this->setFreq(Arguments::arguments.frequency, Arguments::arguments.bandwidth.c_str());
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
// Just skip
|
|
||||||
}
|
|
||||||
|
|
||||||
this->getInterfaceInfo(MONITOR_INTERFACE_NAME);
|
|
||||||
}
|
|
||||||
this->setTxPower();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFIController::createApInteface()
|
|
||||||
{
|
|
||||||
this->addDevice(AP_INTERFACE_NAME, NL80211_IFTYPE_MONITOR);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
||||||
while (this->currentInterfaceInfo.ifType != NL80211_IFTYPE_AP)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this->setInterfaceUpDown(MONITOR_INTERFACE_NAME, false);
|
|
||||||
this->setApMode();
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
// Just skip
|
|
||||||
}
|
|
||||||
|
|
||||||
this->getInterfaceInfo(AP_INTERFACE_NAME);
|
|
||||||
}
|
|
||||||
this->setInterfaceUpDown(AP_INTERFACE_NAME, true);
|
|
||||||
this->setTxPower();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFIController::getPhys()
|
void WiFIController::getPhys()
|
||||||
{
|
{
|
||||||
Cmd cmd{
|
Cmd cmd{
|
||||||
@ -471,10 +414,10 @@ nla_put_failure:
|
|||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiFIController::setTxPowerHandler(nl80211_state *state, nl_msg *msg, void *arg)
|
int WiFIController::setTxPowerHandler(nl80211_state * state, nl_msg * msg, void * arg)
|
||||||
{
|
{
|
||||||
enum nl80211_tx_power_setting type = NL80211_TX_POWER_FIXED;
|
enum nl80211_tx_power_setting type = NL80211_TX_POWER_FIXED;
|
||||||
int mbm = Arguments::arguments.txPower * 100; // dBm to mbm *100
|
int mbm = Arguments::arguments.txPower * 100; //dBm to mbm *100
|
||||||
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_SETTING, type);
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_TX_POWER_LEVEL, mbm);
|
||||||
@ -485,7 +428,7 @@ nla_put_failure:
|
|||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiFIController::processaddDeviceHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
int WiFIController::processAddMonitorDeviceHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
||||||
{
|
{
|
||||||
void **settings = (void **)arg;
|
void **settings = (void **)arg;
|
||||||
const char *name = (const char *)settings[0];
|
const char *name = (const char *)settings[0];
|
||||||
@ -493,8 +436,6 @@ int WiFIController::processaddDeviceHandler(struct nl80211_state *state, struct
|
|||||||
|
|
||||||
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
|
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, name);
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, *type);
|
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, *type);
|
||||||
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, Arguments::arguments.mac);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
nla_put_failure:
|
nla_put_failure:
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
@ -651,104 +592,6 @@ int WiFIController::processGetInterfacesHandler(struct nl_msg *msg, void *arg)
|
|||||||
return NL_OK;
|
return NL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiFIController::processGetInterfaceInfoHandler(struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
|
|
||||||
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
|
|
||||||
|
|
||||||
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
|
||||||
genlmsg_attrlen(gnlh, 0), NULL);
|
|
||||||
WiFIController *wc = (WiFIController *)arg;
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_IFNAME])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifName = nla_get_string(tb_msg[NL80211_ATTR_IFNAME]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifName = "Unnamed/non-netdev interface";
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_IFINDEX])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifIndex = nla_get_u32(tb_msg[NL80211_ATTR_IFINDEX]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_WDEV])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.wdev = (uint64_t)nla_get_u64(tb_msg[NL80211_ATTR_WDEV]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.wdev = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_MAC])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.mac = wc->macN2a((const unsigned char *)nla_data(tb_msg[NL80211_ATTR_MAC]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.mac = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_SSID])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ssid = (const char *)nla_data(tb_msg[NL80211_ATTR_SSID]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ssid = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_IFTYPE])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifType = nla_get_u32(tb_msg[NL80211_ATTR_IFTYPE]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.ifType = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_WIPHY])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.wiphy = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.wiphy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_WIPHY_FREQ])
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.freq = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FREQ]);
|
|
||||||
wc->currentInterfaceInfo.channel = wc->freqToCHannel(wc->currentInterfaceInfo.freq);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.freq = 0;
|
|
||||||
wc->currentInterfaceInfo.channel = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL])
|
|
||||||
{
|
|
||||||
int32_t txp = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_TX_POWER_LEVEL]);
|
|
||||||
wc->currentInterfaceInfo.txdBm = txp % 100;
|
|
||||||
wc->currentInterfaceInfo.txdBm += txp / 100;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wc->currentInterfaceInfo.txdBm = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string WiFIController::macN2a(const unsigned char *arg)
|
std::string WiFIController::macN2a(const unsigned char *arg)
|
||||||
{
|
{
|
||||||
int i, l;
|
int i, l;
|
||||||
@ -817,25 +660,3 @@ int WiFIController::phyLookup(char *name)
|
|||||||
close(fd);
|
close(fd);
|
||||||
return atoi(buf);
|
return atoi(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WiFIController::setApMode()
|
|
||||||
{
|
|
||||||
Cmd cmd{
|
|
||||||
.id = NL80211_CMD_SET_INTERFACE,
|
|
||||||
.idby = CIB_NETDEV,
|
|
||||||
.nlFlags = 0,
|
|
||||||
.device = if_nametoindex(AP_INTERFACE_NAME),
|
|
||||||
.handler = this->setApModeHandler,
|
|
||||||
.valid_handler = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this->nlExecCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFIController::setApModeHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP);
|
|
||||||
return 0;
|
|
||||||
nla_put_failure:
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
||||||
* Copyright (C) 2023-2025 Miroslav Hutar.
|
* Copyright (C) 2023-2024 Miroslav Hutar.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "WiFiCsiController.h"
|
#include "WiFiCsiController.h"
|
||||||
#include "Csi.h"
|
#include "Csi.h"
|
||||||
|
#include "GnuPlot.h"
|
||||||
#include "MainController.h"
|
#include "MainController.h"
|
||||||
#include "Arguments.h"
|
#include "Arguments.h"
|
||||||
|
|
||||||
@ -43,6 +44,8 @@ void WiFiCsiController::init()
|
|||||||
|
|
||||||
int WiFiCsiController::listenToCsi()
|
int WiFiCsiController::listenToCsi()
|
||||||
{
|
{
|
||||||
|
gnuPlot.init();
|
||||||
|
|
||||||
Cmd cmd{
|
Cmd cmd{
|
||||||
.id = NL80211_CMD_VENDOR,
|
.id = NL80211_CMD_VENDOR,
|
||||||
.idby = CIB_NETDEV,
|
.idby = CIB_NETDEV,
|
||||||
@ -66,6 +69,7 @@ nla_put_failure:
|
|||||||
|
|
||||||
int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg)
|
int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg)
|
||||||
{
|
{
|
||||||
|
WiFiCsiController *instance = (WiFiCsiController *)arg;
|
||||||
struct nlattr *attrs[MAX_CMD + 1];
|
struct nlattr *attrs[MAX_CMD + 1];
|
||||||
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||||
|
|
||||||
@ -89,67 +93,50 @@ int WiFiCsiController::processListenToCsiHandler(struct nl_msg *msg, void *arg)
|
|||||||
uint8_t *dataCsi = (uint8_t *)nla_data(attrs[IWL_MVM_VENDOR_ATTR_CSI_DATA]);
|
uint8_t *dataCsi = (uint8_t *)nla_data(attrs[IWL_MVM_VENDOR_ATTR_CSI_DATA]);
|
||||||
memcpy(rawCsi, dataCsi, dataLength);
|
memcpy(rawCsi, dataCsi, dataLength);
|
||||||
|
|
||||||
Csi *c = new Csi();
|
Csi c;
|
||||||
c->loadFromMemory(header, dataCsi);
|
c.loadFromMemory(header, dataCsi);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(c->channelWidth == RATE_MCS_CHAN_WIDTH_20 && Arguments::arguments.channelWidth == 20) ||
|
(c.channelWidth == RATE_MCS_CHAN_WIDTH_20 && Arguments::arguments.channelWidth == 20) ||
|
||||||
(c->channelWidth == RATE_MCS_CHAN_WIDTH_40 && Arguments::arguments.channelWidth == 40) ||
|
(c.channelWidth == RATE_MCS_CHAN_WIDTH_40 && Arguments::arguments.channelWidth == 40) ||
|
||||||
(c->channelWidth == RATE_MCS_CHAN_WIDTH_80 && Arguments::arguments.channelWidth == 80) ||
|
(c.channelWidth == RATE_MCS_CHAN_WIDTH_80 && Arguments::arguments.channelWidth == 80) ||
|
||||||
(c->channelWidth == RATE_MCS_CHAN_WIDTH_160 && Arguments::arguments.channelWidth == 160)
|
(c.channelWidth == RATE_MCS_CHAN_WIDTH_160 && Arguments::arguments.channelWidth == 160)
|
||||||
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
(c->format == RATE_MCS_LEGACY_OFDM_MSK && Arguments::arguments.format == "NOHT") ||
|
(c.format == RATE_MCS_LEGACY_OFDM_MSK && Arguments::arguments.format == "NOHT") ||
|
||||||
(c->format == RATE_MCS_HT_MSK && Arguments::arguments.format == "HT") ||
|
(c.format == RATE_MCS_HT_MSK && Arguments::arguments.format == "HT") ||
|
||||||
(c->format == RATE_MCS_VHT_MSK && Arguments::arguments.format == "VHT") ||
|
(c.format == RATE_MCS_VHT_MSK && Arguments::arguments.format == "VHT") ||
|
||||||
(c->format == RATE_MCS_HE_MSK && Arguments::arguments.format == "HESU") ||
|
(c.format == RATE_MCS_HE_MSK && Arguments::arguments.format == "HESU") ||
|
||||||
(c->format == RATE_MCS_EHT_MSK && Arguments::arguments.format == "EHT")
|
(c.format == RATE_MCS_EHT_MSK && Arguments::arguments.format == "EHT")
|
||||||
|
|
||||||
)
|
)
|
||||||
{
|
|
||||||
printf("masm csi\n");
|
|
||||||
if (!Arguments::arguments.strict || (Arguments::arguments.strict && (c->rawHeaderData.rateNflag & RATE_LEGACY_RATE_MSK) == Arguments::arguments.mcs))
|
|
||||||
{
|
{
|
||||||
if (Arguments::arguments.verbose) {
|
if (Arguments::arguments.verbose) {
|
||||||
printDetail(c);
|
printDetail(c);
|
||||||
}
|
}
|
||||||
|
instance->gnuPlot.updateChart(c);
|
||||||
if ( MainController::getInstance()->udpSocket ) {
|
if ( MainController::getInstance()->udpSocket ) {
|
||||||
c->sendUDP(MainController::getInstance()->udpSocket);
|
c.sendUDP(MainController::getInstance()->udpSocket);
|
||||||
} else {
|
} else {
|
||||||
c->save();
|
c.save();
|
||||||
}
|
|
||||||
if (Arguments::arguments.plot)
|
|
||||||
{
|
|
||||||
WiFiCsiController::csiQueueMutex.lock();
|
|
||||||
WiFiCsiController::csiQueue.push(c);
|
|
||||||
WiFiCsiController::csiQueueMutex.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WiFiCsiController *wcc = (WiFiCsiController*) arg;
|
|
||||||
auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
||||||
if (wcc->stopTime != 0 && wcc->stopTime < now)
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NL_SKIP;
|
return NL_SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiFiCsiController::printDetail(Csi *c)
|
void WiFiCsiController::printDetail(Csi &c)
|
||||||
{
|
{
|
||||||
Logger::log(info) << "Subcarrier count: " << c->rawHeaderData.numSubCarriers << ", ";
|
Logger::log(info) << "Subcarrier count: " << c.rawHeaderData.numSubCarriers << ", ";
|
||||||
Logger::log(info, true) << "RX: " << +c->rawHeaderData.numRx << ", ";
|
Logger::log(info, true) << "RX: " << +c.rawHeaderData.numRx << ", ";
|
||||||
Logger::log(info, true) << "TX: " << +c->rawHeaderData.numTx << ", ";
|
Logger::log(info, true) << "TX: " << +c.rawHeaderData.numTx << ", ";
|
||||||
|
|
||||||
switch (c->channelWidth)
|
switch (c.channelWidth)
|
||||||
{
|
{
|
||||||
case RATE_MCS_CHAN_WIDTH_20:
|
case RATE_MCS_CHAN_WIDTH_20:
|
||||||
Logger::log(info, true) << "Channel width: 20, ";
|
Logger::log(info, true) << "Channel width: 20, ";
|
||||||
@ -164,7 +151,7 @@ void WiFiCsiController::printDetail(Csi *c)
|
|||||||
Logger::log(info, true) << "Channel width: 160, ";
|
Logger::log(info, true) << "Channel width: 160, ";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (c->format)
|
switch (c.format)
|
||||||
{
|
{
|
||||||
case RATE_MCS_CCK_MSK: // VERY OLD FORMAT
|
case RATE_MCS_CCK_MSK: // VERY OLD FORMAT
|
||||||
Logger::log(info, true) << "Format: CCK\n";
|
Logger::log(info, true) << "Format: CCK\n";
|
||||||
@ -172,18 +159,23 @@ void WiFiCsiController::printDetail(Csi *c)
|
|||||||
case RATE_MCS_LEGACY_OFDM_MSK:
|
case RATE_MCS_LEGACY_OFDM_MSK:
|
||||||
Logger::log(info, true) << "Format: LEGACY_OFDM\n";
|
Logger::log(info, true) << "Format: LEGACY_OFDM\n";
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
case RATE_MCS_HT_MSK:
|
case RATE_MCS_HT_MSK:
|
||||||
Logger::log(info, true) << "Format: HT\n";
|
Logger::log(info, true) << "Format: HT\n";
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
case RATE_MCS_VHT_MSK:
|
case RATE_MCS_VHT_MSK:
|
||||||
Logger::log(info, true) << "Format: VHT\n";
|
Logger::log(info, true) << "Format: VHT\n";
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
case RATE_MCS_HE_MSK:
|
case RATE_MCS_HE_MSK:
|
||||||
Logger::log(info, true) << "Format: HE\n";
|
Logger::log(info, true) << "Format: HE\n";
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
case RATE_MCS_EHT_MSK:
|
case RATE_MCS_EHT_MSK:
|
||||||
Logger::log(info, true) << "Format: EHT\n";
|
Logger::log(info, true) << "Format: EHT\n";
|
||||||
break;
|
break;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,339 +0,0 @@
|
|||||||
/*
|
|
||||||
* FeitCSI is the tool for extracting CSI information from supported intel NICs.
|
|
||||||
* Copyright (C) 2025 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 "WiFiFtmController.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "Arguments.h"
|
|
||||||
#include <net/if.h>
|
|
||||||
#include "Logger.h"
|
|
||||||
#include "MainController.h"
|
|
||||||
#include <netlink/genl/genl.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
uint8_t beaconHeader[] = {0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0xb9, 0xcc, 0xc2, 0x4a, 0xaf, 0xea, 0xb9, 0xcc, 0xc2, 0x4a, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x01, 0x04, 0x00, 0x07, 0x46, 0x65, 0x69, 0x74, 0x43, 0x53, 0x49, 0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x03, 0x01, 0x01};
|
|
||||||
|
|
||||||
void WiFiFtmController::init()
|
|
||||||
{
|
|
||||||
Netlink::init();
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::startInitiator()
|
|
||||||
{
|
|
||||||
Cmd cmd{
|
|
||||||
.id = NL80211_CMD_PEER_MEASUREMENT_START,
|
|
||||||
.idby = CIB_NETDEV,
|
|
||||||
.nlFlags = 0,
|
|
||||||
.device = if_nametoindex(AP_INTERFACE_NAME),
|
|
||||||
.handler = this->ftmHandler,
|
|
||||||
.valid_handler = this->processFtmHandler,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this->nlExecCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::startResponder()
|
|
||||||
{
|
|
||||||
Cmd cmd{
|
|
||||||
.id = NL80211_CMD_NEW_BEACON,
|
|
||||||
.idby = CIB_NETDEV,
|
|
||||||
.nlFlags = 0,
|
|
||||||
.device = if_nametoindex(AP_INTERFACE_NAME),
|
|
||||||
.handler = this->ftmResponderHandler,
|
|
||||||
.valid_handler = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this->nlExecCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::setApMode()
|
|
||||||
{
|
|
||||||
Cmd cmd{
|
|
||||||
.id = NL80211_CMD_SET_INTERFACE,
|
|
||||||
.idby = CIB_NETDEV,
|
|
||||||
.nlFlags = 0,
|
|
||||||
.device = if_nametoindex(AP_INTERFACE_NAME),
|
|
||||||
.handler = this->setApModeHandler,
|
|
||||||
.valid_handler = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this->nlExecCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::ftmHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct nlattr *pmsr, *peers, *peer, *req, *reqdata, *ftm, *chan;
|
|
||||||
|
|
||||||
pmsr = nla_nest_start(msg, NL80211_ATTR_PEER_MEASUREMENTS);
|
|
||||||
peers = nla_nest_start(msg, NL80211_PMSR_ATTR_PEERS);
|
|
||||||
|
|
||||||
peer = nla_nest_start(msg, 1); // TODO multiple peers
|
|
||||||
NLA_PUT(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, Arguments::arguments.ftmTargetMac);
|
|
||||||
|
|
||||||
req = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_REQ);
|
|
||||||
reqdata = nla_nest_start(msg, NL80211_PMSR_REQ_ATTR_DATA);
|
|
||||||
ftm = nla_nest_start(msg, NL80211_PMSR_TYPE_FTM);
|
|
||||||
|
|
||||||
if (Arguments::arguments.ftmBurstExp)
|
|
||||||
{
|
|
||||||
NLA_PUT_U8(msg, NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, Arguments::arguments.ftmBurstExp);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmBurstPeriod)
|
|
||||||
{
|
|
||||||
NLA_PUT_U16(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, Arguments::arguments.ftmBurstPeriod);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmBurstDuration)
|
|
||||||
{
|
|
||||||
NLA_PUT_U8(msg, NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, Arguments::arguments.ftmBurstDuration);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmPerBurst)
|
|
||||||
{
|
|
||||||
NLA_PUT_U8(msg, NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, Arguments::arguments.ftmPerBurst);
|
|
||||||
}
|
|
||||||
if (Arguments::arguments.ftmAsap)
|
|
||||||
{
|
|
||||||
NLA_PUT_FLAG(msg, NL80211_PMSR_FTM_REQ_ATTR_ASAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Arguments::arguments.format == "NOHT")
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, NL80211_PREAMBLE_LEGACY);
|
|
||||||
}
|
|
||||||
else if (Arguments::arguments.format == "HT")
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, NL80211_PREAMBLE_HT);
|
|
||||||
}
|
|
||||||
else if (Arguments::arguments.format == "VHT")
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, NL80211_PREAMBLE_VHT);
|
|
||||||
}
|
|
||||||
else if (Arguments::arguments.format == "HESU")
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, NL80211_PREAMBLE_HE);
|
|
||||||
}
|
|
||||||
|
|
||||||
nla_nest_end(msg, ftm);
|
|
||||||
nla_nest_end(msg, reqdata);
|
|
||||||
nla_nest_end(msg, req);
|
|
||||||
|
|
||||||
chan = nla_nest_start(msg, NL80211_PMSR_PEER_ATTR_CHAN);
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, (uint32_t)Arguments::arguments.frequency);
|
|
||||||
|
|
||||||
switch (Arguments::arguments.channelWidth)
|
|
||||||
{
|
|
||||||
case 20:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_20);
|
|
||||||
break;
|
|
||||||
case 40:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_40);
|
|
||||||
break;
|
|
||||||
case 80:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80);
|
|
||||||
break;
|
|
||||||
case 160:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160);
|
|
||||||
break;
|
|
||||||
case 320:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_320);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
nla_nest_end(msg, chan);
|
|
||||||
nla_nest_end(msg, peer);
|
|
||||||
nla_nest_end(msg, peers);
|
|
||||||
nla_nest_end(msg, pmsr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
nla_put_failure:
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::ftmResponderHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, 58, beaconHeader);
|
|
||||||
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, 100);
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, 2);
|
|
||||||
NLA_PUT(msg, NL80211_ATTR_SSID, 7, "FeitCSI");
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, NL80211_HIDDEN_SSID_NOT_IN_USE);
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, NL80211_AUTHTYPE_OPEN_SYSTEM);
|
|
||||||
|
|
||||||
struct nlattr *ftm;
|
|
||||||
ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER);
|
|
||||||
NLA_PUT_FLAG(msg, NL80211_FTM_RESP_ATTR_ENABLED);
|
|
||||||
nla_nest_end(msg, ftm);
|
|
||||||
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, (uint32_t)Arguments::arguments.frequency);
|
|
||||||
switch (Arguments::arguments.channelWidth)
|
|
||||||
{
|
|
||||||
case 20:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_20);
|
|
||||||
break;
|
|
||||||
case 40:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_40);
|
|
||||||
break;
|
|
||||||
case 80:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_80);
|
|
||||||
break;
|
|
||||||
case 160:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_160);
|
|
||||||
break;
|
|
||||||
case 320:
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, NL80211_CHAN_WIDTH_320);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
NLA_PUT_FLAG(msg, NL80211_ATTR_SOCKET_OWNER);
|
|
||||||
|
|
||||||
// nl_msg_dump(msg, stdout);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
nla_put_failure:
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::setApModeHandler(struct nl80211_state *state, struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP);
|
|
||||||
return 0;
|
|
||||||
nla_put_failure:
|
|
||||||
return -ENOBUFS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiFtmController::processFtmHandler(struct nl_msg *msg, void *arg)
|
|
||||||
{
|
|
||||||
struct genlmsghdr *gnlh = (genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
|
|
||||||
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
|
||||||
|
|
||||||
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
|
|
||||||
|
|
||||||
if (gnlh->cmd == NL80211_CMD_PEER_MEASUREMENT_COMPLETE)
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gnlh->cmd == NL80211_CMD_PEER_MEASUREMENT_RESULT)
|
|
||||||
{
|
|
||||||
if (!tb[NL80211_ATTR_PEER_MEASUREMENTS])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
struct nlattr *pmsr[NL80211_PMSR_ATTR_MAX + 1];
|
|
||||||
nla_parse_nested(pmsr, NL80211_PMSR_ATTR_MAX, tb[NL80211_ATTR_PEER_MEASUREMENTS], NULL);
|
|
||||||
|
|
||||||
if (!pmsr[NL80211_PMSR_ATTR_PEERS])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct nlattr *peer = (nlattr *)nla_data(pmsr[NL80211_PMSR_ATTR_PEERS]);
|
|
||||||
|
|
||||||
struct nlattr *tbpeer[NL80211_PMSR_PEER_ATTR_MAX + 1];
|
|
||||||
struct nlattr *resp[NL80211_PMSR_RESP_ATTR_MAX + 1];
|
|
||||||
struct nlattr *data[NL80211_PMSR_TYPE_MAX + 1];
|
|
||||||
|
|
||||||
nla_parse_nested(tbpeer, NL80211_PMSR_PEER_ATTR_MAX, peer, NULL);
|
|
||||||
|
|
||||||
if (!tbpeer[NL80211_PMSR_PEER_ATTR_ADDR])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t mac[ETH_ALEN];
|
|
||||||
memcpy(mac, nla_data(tbpeer[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
|
|
||||||
|
|
||||||
if (!tbpeer[NL80211_PMSR_PEER_ATTR_ADDR])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nla_parse_nested(resp, NL80211_PMSR_RESP_ATTR_MAX, tbpeer[NL80211_PMSR_PEER_ATTR_RESP], NULL);
|
|
||||||
|
|
||||||
if (!resp[NL80211_PMSR_RESP_ATTR_DATA])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nla_parse_nested(data, NL80211_PMSR_TYPE_MAX, resp[NL80211_PMSR_RESP_ATTR_DATA], NULL);
|
|
||||||
|
|
||||||
if (!data[NL80211_PMSR_TYPE_FTM])
|
|
||||||
{
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct nlattr *ftm[NL80211_PMSR_FTM_RESP_ATTR_MAX + 1];
|
|
||||||
nla_parse_nested(ftm, NL80211_PMSR_FTM_RESP_ATTR_MAX, data[NL80211_PMSR_TYPE_FTM], NULL);
|
|
||||||
WiFiFtmController *wfc = (WiFiFtmController *)arg;
|
|
||||||
if (ftm[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON])
|
|
||||||
{
|
|
||||||
uint32_t reason = nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON]);
|
|
||||||
if (Arguments::arguments.verbose)
|
|
||||||
{
|
|
||||||
Logger::log(info) << "FTM failed to measure " << reason << "\n";
|
|
||||||
}
|
|
||||||
wfc->lastRttIsSuccess = false;
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ftm ftmData{
|
|
||||||
.burstIndex = (uint32_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX] ? nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX]) : 0),
|
|
||||||
.numFtmrAttempts = (uint32_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS] ? nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS]) : 0),
|
|
||||||
.numFtmrSuccesses = (uint32_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES] ? nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES]) : 0),
|
|
||||||
.numBurstsExp = (uint32_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP] ? nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP]) : 0),
|
|
||||||
.burstDuration = (uint8_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION] ? nla_get_u8(ftm[NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION]) : 0),
|
|
||||||
.ftmsPerBurst = (uint8_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST] ? nla_get_u8(ftm[NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST]) : 0),
|
|
||||||
.rssiAvg = (uint8_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG] ? nla_get_u8(ftm[NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG]) : 0),
|
|
||||||
.rssiSpread = (uint32_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD] ? nla_get_u32(ftm[NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD]) : 0),
|
|
||||||
.rttAvg = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG]) : 0),
|
|
||||||
.rttVariance = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE]) : 0),
|
|
||||||
.rttSpread = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD]) : 0),
|
|
||||||
.distAvg = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG]) : 0),
|
|
||||||
.distVariance = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE]) : 0),
|
|
||||||
.distSpread = (uint64_t)(ftm[NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD] ? nla_get_u64(ftm[NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD]) : 0),
|
|
||||||
.timestamp = (uint64_t)std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Arguments::arguments.verbose)
|
|
||||||
{
|
|
||||||
Logger::log(info) << "FTM average RTT: " << ftmData.rttAvg << "ps\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MainController::getInstance()->udpSocket)
|
|
||||||
{
|
|
||||||
MainController::getInstance()->udpSocket->send(reinterpret_cast<char *>(&ftmData), FTM_SIZE);
|
|
||||||
} else {
|
|
||||||
std::ofstream outfile;
|
|
||||||
std::string filename = std::string("FTM_") + Arguments::arguments.outputFile;
|
|
||||||
outfile.open(filename, std::ios_base::app | std::ios::binary);
|
|
||||||
if (outfile.fail())
|
|
||||||
{
|
|
||||||
throw std::ios_base::failure("Open file failed: " + std::string(std::strerror(errno)));
|
|
||||||
}
|
|
||||||
outfile.write(reinterpret_cast<char *>(&ftmData), FTM_SIZE);
|
|
||||||
outfile.close();
|
|
||||||
std::filesystem::permissions(filename, std::filesystem::perms::all & ~(std::filesystem::perms::owner_exec | std::filesystem::perms::group_exec | std::filesystem::perms::others_exec), std::filesystem::perm_options::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
wfc->lastRttIsSuccess = true;
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NL_OK;
|
|
||||||
}
|
|
||||||
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/CsiProcessingWindow.h"
|
#include "gui/CsiProcessingWindow.h"
|
||||||
|
#include "GnuPlot.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Arguments.h"
|
#include "Arguments.h"
|
||||||
@ -64,10 +65,10 @@ void CsiProcessingWindow::refresh()
|
|||||||
|
|
||||||
this->currentDataCount->set_text(indexLabelText);
|
this->currentDataCount->set_text(indexLabelText);
|
||||||
|
|
||||||
/* if (!this->csiProcessor.csiData.empty())
|
if (!this->csiProcessor.csiData.empty())
|
||||||
{
|
{
|
||||||
gnuPlot.updateChartAsync(this->csiProcessor.csiData[this->currentIndex]);
|
gnuPlot.updateChart(*this->csiProcessor.csiData[this->currentIndex]);
|
||||||
} */
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
138
src/gui/GnuPlot.cpp
Normal file
138
src/gui/GnuPlot.cpp
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 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::updateChart(Csi &csi)
|
||||||
|
{
|
||||||
|
if (!Arguments::arguments.plot)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GnuPlot::reload()
|
||||||
|
{
|
||||||
|
fprintf(this->gnuPlotPipe, this->lastCmd.c_str());
|
||||||
|
fflush(this->gnuPlotPipe);
|
||||||
|
}
|
||||||
@ -29,16 +29,6 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
|||||||
set_title("FeitCSI");
|
set_title("FeitCSI");
|
||||||
set_default_size(1000, 800);
|
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 = Glib::RefPtr<Gtk::Button>::cast_dynamic(this->builder->get_object("measureButton"));
|
||||||
this->measureButton->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::measureButtonClicked));
|
this->measureButton->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::measureButtonClicked));
|
||||||
|
|
||||||
@ -90,6 +80,8 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
|||||||
|
|
||||||
this->errorMessages = Glib::RefPtr<Gtk::Label>::cast_dynamic(this->builder->get_object("errorMessages"));
|
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"));
|
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"));
|
Glib::RefPtr<Gtk::ScrolledWindow> consoleScrollbar = Glib::RefPtr<Gtk::ScrolledWindow>::cast_dynamic(this->builder->get_object("consoleScrollbar"));
|
||||||
this->consoleScrollbarAdjustment = consoleScrollbar->get_vadjustment();
|
this->consoleScrollbarAdjustment = consoleScrollbar->get_vadjustment();
|
||||||
@ -100,9 +92,12 @@ MainWindow::MainWindow(BaseObjectType *obj, Glib::RefPtr<Gtk::Builder> const &bu
|
|||||||
|
|
||||||
Logger::guiOutput = this->console->get_buffer();
|
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_check_resize().connect_notify(sigc::mem_fun(*this, &MainWindow::onResize));
|
||||||
|
|
||||||
this->signal_show().connect(sigc::mem_fun(*this, &MainWindow::onWindowInit));
|
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);
|
this->csiProcessingWindow.init(this->builder);
|
||||||
}
|
}
|
||||||
@ -123,14 +118,16 @@ bool MainWindow::onWindowDelete(GdkEventAny *event)
|
|||||||
{
|
{
|
||||||
MainController *mainController = MainController::getInstance();
|
MainController *mainController = MainController::getInstance();
|
||||||
mainController->deleteInstance();
|
mainController->deleteInstance();
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::onWindowInit()
|
void MainWindow::onWindowInit()
|
||||||
{
|
{
|
||||||
Glib::RefPtr<Gtk::Paned> socketConsolePaned = Glib::RefPtr<Gtk::Paned>::cast_dynamic(this->builder->get_object("socketConsolePaned"));
|
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);
|
socketConsolePaned->set_position(socketConsolePaned->get_allocated_height() * 0.8);
|
||||||
|
MainController *mainController = MainController::getInstance();
|
||||||
|
mainController->initInterface();
|
||||||
|
this->gnuPlot.setBlank();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onResize()
|
void MainWindow::onResize()
|
||||||
@ -140,6 +137,21 @@ void MainWindow::onResize()
|
|||||||
this->consoleScrollbarAdjustment->set_value(this->consoleScrollbarAdjustment->get_upper());
|
this->consoleScrollbarAdjustment->set_value(this->consoleScrollbarAdjustment->get_upper());
|
||||||
return;
|
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()
|
void MainWindow::frequencyChange()
|
||||||
|
|||||||
175
src/gui/Plot.cpp
175
src/gui/Plot.cpp
@ -1,175 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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