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