Forgot about version control, here's the whole code at once
This commit is contained in:
commit
228fc3ab24
25
LivePow.pro
Normal file
25
LivePow.pro
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
QT += core gui
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++11
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
displayarea.cpp \
|
||||||
|
inputparser.cpp \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
displayarea.h \
|
||||||
|
inputparser.h \
|
||||||
|
mainwindow.h
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# LivePow
|
||||||
|
An reader for making heatmap from soapy_power output
|
||||||
|
|
||||||
|
## Building
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
qmake ..
|
||||||
|
make -j4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### Example scanning the FM radio band
|
||||||
|
`soapy_power -f 80M:110M -n 10 -e 30 -B 30k -k 30 --pow2 -F rtl_power -R | ./Livepow`
|
47
displayarea.cpp
Normal file
47
displayarea.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "displayarea.h"
|
||||||
|
#include <qpainter.h>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
DisplayArea::DisplayArea(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_StaticContents);
|
||||||
|
|
||||||
|
QTimer* timer = new QTimer(this);
|
||||||
|
connect(timer,SIGNAL(timeout()),this,SLOT(updateDisp()));
|
||||||
|
timer->start(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayArea::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
QPainter painter(this);
|
||||||
|
QRect rect=event->rect();
|
||||||
|
painter.drawImage(rect,image,rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayArea::setPixel(int x, int y, unsigned int col)
|
||||||
|
{
|
||||||
|
if(x>=image.width()||y>=image.height()) resizeImage(&image,QSize(qMax(x+255,image.width()),qMax(y+255,image.height())));
|
||||||
|
image.setPixel(x,y,col);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayArea::resizeImage(QImage *image, const QSize &newSize)
|
||||||
|
{
|
||||||
|
if(image->size()==newSize) return;
|
||||||
|
|
||||||
|
QImage newImage(newSize,QImage::Format_ARGB32);
|
||||||
|
newImage.fill(qRgba(255,255,255,0));
|
||||||
|
QPainter painter(&newImage);
|
||||||
|
painter.drawImage(QPoint(0,0),*image);
|
||||||
|
*image=newImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisplayArea::updateDisp()
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DisplayArea::saveImage(const QString &fileName, const char *fileFormat)
|
||||||
|
{
|
||||||
|
return image.save(fileName, fileFormat);
|
||||||
|
}
|
29
displayarea.h
Normal file
29
displayarea.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef DISPLAYAREA_H
|
||||||
|
#define DISPLAYAREA_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class DisplayArea : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
DisplayArea(QWidget *parent = nullptr);
|
||||||
|
void setPixel(int x,int y,unsigned int col);
|
||||||
|
bool saveImage(const QString &fileName, const char *fileFormat);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resizeImage(QImage *image, const QSize &newSize);
|
||||||
|
|
||||||
|
QImage image;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateDisp();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DISPLAYAREA_H
|
132
inputparser.cpp
Normal file
132
inputparser.cpp
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include "inputparser.h"
|
||||||
|
#include <QTimer>
|
||||||
|
#include "displayarea.h"
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
InputParser::InputParser(FILE* input,DisplayArea* display)
|
||||||
|
{
|
||||||
|
InputParser::input=input;
|
||||||
|
|
||||||
|
InputParser::display=display;
|
||||||
|
|
||||||
|
random=new QRandomGenerator();
|
||||||
|
|
||||||
|
/*QTimer* timer = new QTimer(this);
|
||||||
|
connect(timer,SIGNAL(timeout()),this,SLOT(process()));
|
||||||
|
timer->start(0);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InputParser::process()
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
int c=fgetc(input);
|
||||||
|
if(c==EOF) continue;
|
||||||
|
|
||||||
|
switch(c){
|
||||||
|
case ',':
|
||||||
|
switch(index){
|
||||||
|
case 1:
|
||||||
|
computeEpochDate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(index>5){
|
||||||
|
sendPixel();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
decimal=false;
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
index=0;
|
||||||
|
minFreq=qMin(minFreq,currentLine.minFreq);
|
||||||
|
maxFreq=qMax(maxFreq,currentLine.maxFreq);
|
||||||
|
|
||||||
|
sendPixel();
|
||||||
|
|
||||||
|
if(currentLine.maxFreq<=lastMaxFreq){
|
||||||
|
currentY++;
|
||||||
|
currentX=0;
|
||||||
|
}
|
||||||
|
lastMaxFreq=currentLine.maxFreq;
|
||||||
|
|
||||||
|
currentLine=line();
|
||||||
|
decimal=false;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
decimal=true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
switch(index){
|
||||||
|
case 0:
|
||||||
|
addToString(c,¤tLine.firstDate);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
addToString(c,¤tLine.secondDate);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
addToInt(c,¤tLine.minFreq);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
addToInt(c,¤tLine.maxFreq);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
addToInt(c,¤tLine.freqStep);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if(index>5) addToFloat(c,¤tPowerValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//printf("%c aaa");
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputParser::addToString(char c, QString* str)
|
||||||
|
{
|
||||||
|
str->append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputParser::addToInt(char c, unsigned long* nb)
|
||||||
|
{
|
||||||
|
if(decimal||c<'0'||c>'9') return;
|
||||||
|
*nb*=10;
|
||||||
|
*nb+=(c-'0');
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputParser::addToFloat(char c, float* nb)
|
||||||
|
{
|
||||||
|
if(c<'0'||c>'9') return;
|
||||||
|
if(!decimal){
|
||||||
|
*nb*=10;
|
||||||
|
*nb+=(c-'0');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
float inc=((c-'0')/pow(10,decimalIndex));
|
||||||
|
*nb+=inc;
|
||||||
|
decimalIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputParser::computeEpochDate()
|
||||||
|
{
|
||||||
|
QString dateMerged=QString();
|
||||||
|
dateMerged.append(currentLine.firstDate);
|
||||||
|
dateMerged.append(currentLine.secondDate);
|
||||||
|
//2021-05-28 23:20:36
|
||||||
|
currentLine.parsedDate=QDateTime::fromString(dateMerged,"yyyy-MM-dd hh:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputParser::sendPixel()
|
||||||
|
{
|
||||||
|
currentPowerValue*=sign;
|
||||||
|
int col=qRound((80-currentPowerValue)*6);
|
||||||
|
display->setPixel(currentX,currentY,qRgba(col,col,col,255));
|
||||||
|
currentX++;
|
||||||
|
currentPowerValue=0;
|
||||||
|
decimalIndex=1;
|
||||||
|
sign=1;
|
||||||
|
}
|
63
inputparser.h
Normal file
63
inputparser.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef INPUTPARSER_H
|
||||||
|
#define INPUTPARSER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <QTimerEvent>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
class DisplayArea;
|
||||||
|
|
||||||
|
struct line{
|
||||||
|
QString firstDate;
|
||||||
|
QString secondDate;
|
||||||
|
QDateTime parsedDate;
|
||||||
|
unsigned long minFreq=0;
|
||||||
|
unsigned long maxFreq=0;
|
||||||
|
unsigned long freqStep=0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputParser : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
InputParser(FILE* input,DisplayArea* display);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned long minFreq=-1;
|
||||||
|
unsigned long maxFreq=0;
|
||||||
|
|
||||||
|
unsigned long lastMaxFreq=0;
|
||||||
|
|
||||||
|
unsigned long index=0;
|
||||||
|
|
||||||
|
line currentLine;
|
||||||
|
|
||||||
|
FILE* input;
|
||||||
|
|
||||||
|
bool decimal=false;
|
||||||
|
int decimalIndex=1;
|
||||||
|
int sign=1;
|
||||||
|
|
||||||
|
unsigned int currentX=0;
|
||||||
|
unsigned int currentY=0;
|
||||||
|
|
||||||
|
DisplayArea* display;
|
||||||
|
|
||||||
|
QRandomGenerator* random;
|
||||||
|
|
||||||
|
float currentPowerValue=0;
|
||||||
|
|
||||||
|
void addToString(char c,QString* str);
|
||||||
|
void addToInt(char c,unsigned long* nb);
|
||||||
|
void addToFloat(char c,float* nb);
|
||||||
|
void computeEpochDate();
|
||||||
|
|
||||||
|
void sendPixel();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void process();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INPUTPARSER_H
|
11
main.cpp
Normal file
11
main.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
return a.exec();
|
||||||
|
}
|
82
mainwindow.cpp
Normal file
82
mainwindow.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "displayarea.h"
|
||||||
|
#include "inputparser.h"
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QImageWriter>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QMenuBar>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent),displayArea(new DisplayArea(this))
|
||||||
|
{
|
||||||
|
setCentralWidget(displayArea);
|
||||||
|
|
||||||
|
FILE* input=stdin;
|
||||||
|
|
||||||
|
parser=new InputParser(input,displayArea);
|
||||||
|
|
||||||
|
QThread* workerThread=new QThread();
|
||||||
|
|
||||||
|
parser->moveToThread(workerThread);
|
||||||
|
connect(workerThread, &QThread::started, parser, &InputParser::process);
|
||||||
|
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
||||||
|
workerThread->start();
|
||||||
|
|
||||||
|
createActions();
|
||||||
|
createMenus();
|
||||||
|
|
||||||
|
setWindowTitle(tr("LivePow"));
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::save(){
|
||||||
|
QAction *action = qobject_cast<QAction *>(sender());
|
||||||
|
QByteArray fileFormat = action->data().toByteArray();
|
||||||
|
saveFile(fileFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainWindow::saveFile(const QByteArray &fileFormat)
|
||||||
|
{
|
||||||
|
QString initialPath = QDir::currentPath() + "/untitled." + fileFormat;
|
||||||
|
|
||||||
|
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
|
||||||
|
initialPath,
|
||||||
|
tr("%1 Files (*.%2);;All Files (*)")
|
||||||
|
.arg(QString::fromLatin1(fileFormat.toUpper()),QString::fromLatin1(fileFormat)));
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return false;
|
||||||
|
return displayArea->saveImage(fileName, fileFormat.constData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::createActions()
|
||||||
|
{
|
||||||
|
const QList<QByteArray> imageFormats = QImageWriter::supportedImageFormats();
|
||||||
|
for (const QByteArray &format : imageFormats) {
|
||||||
|
QString text = tr("%1...").arg(QString::fromLatin1(format).toUpper());
|
||||||
|
|
||||||
|
QAction *action = new QAction(text, this);
|
||||||
|
action->setData(format);
|
||||||
|
connect(action, &QAction::triggered, this, &MainWindow::save);
|
||||||
|
saveAsActs.append(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::createMenus()
|
||||||
|
{
|
||||||
|
saveAsMenu = new QMenu(tr("&Save As"), this);
|
||||||
|
for (QAction *action : qAsConst(saveAsActs))
|
||||||
|
saveAsMenu->addAction(action);
|
||||||
|
|
||||||
|
fileMenu = new QMenu(tr("&File"), this);
|
||||||
|
fileMenu->addMenu(saveAsMenu);
|
||||||
|
|
||||||
|
menuBar()->addMenu(fileMenu);
|
||||||
|
}
|
39
mainwindow.h
Normal file
39
mainwindow.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
class DisplayArea;
|
||||||
|
class InputParser;
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
static void tick();
|
||||||
|
|
||||||
|
InputParser *parser;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DisplayArea *displayArea;
|
||||||
|
void createActions();
|
||||||
|
void createMenus();
|
||||||
|
|
||||||
|
QMenu *saveAsMenu;
|
||||||
|
QMenu *fileMenu;
|
||||||
|
|
||||||
|
QList<QAction *> saveAsActs;
|
||||||
|
|
||||||
|
bool saveFile(const QByteArray &fileFormat);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void save();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void operate(const QString &);
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
Loading…
x
Reference in New Issue
Block a user