mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-22 14:55:36 +08:00
AFV initial commit
This commit is contained in:
committed by
Mat Sutcliffe
parent
7030302e73
commit
b5a2f2ad13
42
samples/afvclient/afvclient.pro
Normal file
42
samples/afvclient/afvclient.pro
Normal file
@@ -0,0 +1,42 @@
|
||||
load(common_pre)
|
||||
|
||||
QT += dbus network multimedia gui quick
|
||||
|
||||
CONFIG += c++14
|
||||
CONFIG -= app_bundle
|
||||
CONFIG += blackmisc blackcore blackconfig
|
||||
|
||||
DEPENDPATH += . $$SourceRoot/src/blackmisc
|
||||
INCLUDEPATH += . $$SourceRoot/src
|
||||
|
||||
INCLUDEPATH += $$SourceRoot/src/blackcore/afv
|
||||
INCLUDEPATH += $$SourceRoot/src/blackmisc/network
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
afvmapreader.cpp \
|
||||
models/atcstationmodel.cpp \
|
||||
main.cpp \
|
||||
|
||||
HEADERS += \
|
||||
models/atcstationmodel.h \
|
||||
afvmapreader.h \
|
||||
|
||||
DEFINES += _USE_MATH_DEFINES
|
||||
|
||||
RESOURCES += \
|
||||
qml/qml.qrc
|
||||
|
||||
DESTDIR = $$DestRoot/bin
|
||||
|
||||
load(common_post)
|
||||
89
samples/afvclient/afvmapreader.cpp
Normal file
89
samples/afvclient/afvmapreader.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "afvmapreader.h"
|
||||
#include "blackcore/application.h"
|
||||
#include "dto.h"
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
AFVMapReader::AFVMapReader(QObject *parent) : QObject(parent)
|
||||
{
|
||||
model = new AtcStationModel(this);
|
||||
timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, &AFVMapReader::updateFromMap);
|
||||
timer->start(3000);
|
||||
}
|
||||
|
||||
void AFVMapReader::updateFromMap()
|
||||
{
|
||||
if (! sApp) { return; }
|
||||
|
||||
QEventLoop loop;
|
||||
connect(sApp->getNetworkAccessManager(), &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
|
||||
QNetworkReply *reply = sApp->getNetworkAccessManager()->get(QNetworkRequest(QUrl("https://afv-map.vatsim.net/atis-map-data")));
|
||||
while (! reply->isFinished()) { loop.exec(); }
|
||||
QByteArray jsonData = reply->readAll();
|
||||
reply->deleteLater();
|
||||
|
||||
if (jsonData.isEmpty()) { return; }
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
|
||||
if (jsonDoc.isObject())
|
||||
{
|
||||
QJsonObject rootObject = jsonDoc.object();
|
||||
QVector<AtcStation> transceivers;
|
||||
|
||||
if (rootObject.contains("controllers"))
|
||||
{
|
||||
QJsonObject otherObject = rootObject.value("controllers").toObject();
|
||||
for (auto it = otherObject.begin(); it != otherObject.end(); ++it)
|
||||
{
|
||||
QString callsign = it.key();
|
||||
if (it.value().isObject())
|
||||
{
|
||||
QJsonObject stationObject = it.value().toObject();
|
||||
if (stationObject.contains("transceivers"))
|
||||
{
|
||||
QJsonArray txArray = stationObject.value("transceivers").toArray();
|
||||
for (auto jt = txArray.begin(); jt != txArray.end(); ++jt)
|
||||
{
|
||||
TransceiverDto transceiver = TransceiverDto::fromJson(jt->toObject());
|
||||
transceivers.push_back( { callsign, transceiver} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rootObject.contains("other") && rootObject.value("other").isObject())
|
||||
{
|
||||
QJsonObject otherObject = rootObject.value("other").toObject();
|
||||
for (auto it = otherObject.begin(); it != otherObject.end(); ++it)
|
||||
{
|
||||
QString callsign = it.key();
|
||||
if (it.value().isObject())
|
||||
{
|
||||
QJsonObject stationObject = it.value().toObject();
|
||||
if (stationObject.contains("transceivers"))
|
||||
{
|
||||
QJsonArray txArray = stationObject.value("transceivers").toArray();
|
||||
for (auto jt = txArray.begin(); jt != txArray.end(); ++jt)
|
||||
{
|
||||
TransceiverDto transceiver = TransceiverDto::fromJson(jt->toObject());
|
||||
transceivers.push_back( { callsign, transceiver} );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transceivers.isEmpty()) { return; }
|
||||
transceivers.erase(std::remove_if(transceivers.begin(), transceivers.end(), [this](const AtcStation &s)
|
||||
{
|
||||
return s.callsign() == m_callsign;
|
||||
}),
|
||||
transceivers.end());
|
||||
model->updateAtcStations(transceivers);
|
||||
}
|
||||
}
|
||||
28
samples/afvclient/afvmapreader.h
Normal file
28
samples/afvclient/afvmapreader.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef AFVMAPREADER_H
|
||||
#define AFVMAPREADER_H
|
||||
|
||||
#include "models/atcstationmodel.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
class AFVMapReader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(AtcStationModel* atcStationModel READ getAtcStationModel CONSTANT)
|
||||
public:
|
||||
AFVMapReader(QObject *parent = nullptr);
|
||||
|
||||
Q_INVOKABLE void setOwnCallsign(const QString &callsign) { m_callsign = callsign; }
|
||||
|
||||
void updateFromMap();
|
||||
|
||||
AtcStationModel *getAtcStationModel() { return model; }
|
||||
|
||||
private:
|
||||
AtcStationModel *model = nullptr;
|
||||
QTimer *timer = nullptr;
|
||||
QString m_callsign;
|
||||
};
|
||||
|
||||
#endif // AFVMAPREADER_H
|
||||
32
samples/afvclient/main.cpp
Normal file
32
samples/afvclient/main.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
// #include "voiceclientui.h"
|
||||
#include "models/atcstationmodel.h"
|
||||
#include "clients/afvclient.h"
|
||||
#include "afvmapreader.h"
|
||||
#include "blackcore/application.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication qa(argc, argv);
|
||||
|
||||
BlackCore::CApplication a("sampleafvclient", BlackMisc::CApplicationInfo::Sample);
|
||||
|
||||
AFVMapReader *afvMapReader = new AFVMapReader(&a);
|
||||
afvMapReader->updateFromMap();
|
||||
|
||||
AFVClient voiceClient("https://voice1.vatsim.uk");
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
QQmlContext *ctxt = engine.rootContext();
|
||||
ctxt->setContextProperty("afvMapReader", afvMapReader);
|
||||
ctxt->setContextProperty("voiceClient", &voiceClient);
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
122
samples/afvclient/models/atcstationmodel.cpp
Normal file
122
samples/afvclient/models/atcstationmodel.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "atcstationmodel.h"
|
||||
#include "dto.h"
|
||||
#include <QtMath>
|
||||
|
||||
AtcStation::AtcStation(const QString &callsign, const TransceiverDto &transceiver) :
|
||||
m_callsign(callsign),
|
||||
m_transceiver(transceiver)
|
||||
{ }
|
||||
|
||||
double AtcStation::latitude() const
|
||||
{
|
||||
return m_transceiver.LatDeg;
|
||||
}
|
||||
|
||||
double AtcStation::longitude() const
|
||||
{
|
||||
return m_transceiver.LonDeg;
|
||||
}
|
||||
|
||||
quint32 AtcStation::frequency() const
|
||||
{
|
||||
return m_transceiver.frequency;
|
||||
}
|
||||
|
||||
|
||||
QString AtcStation::formattedFrequency() const
|
||||
{
|
||||
return QString::number(m_transceiver.frequency / 1000000.0, 'f', 3);
|
||||
}
|
||||
|
||||
double AtcStation::radioDistanceM() const
|
||||
{
|
||||
double sqrtAltM = qSqrt(m_transceiver.HeightMslM);
|
||||
const double radioFactor = 4193.18014745372;
|
||||
|
||||
return radioFactor * sqrtAltM;
|
||||
}
|
||||
|
||||
QString AtcStation::callsign() const
|
||||
{
|
||||
return m_callsign;
|
||||
}
|
||||
|
||||
AtcStationModel::AtcStationModel(QObject *parent) :
|
||||
QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AtcStationModel::~AtcStationModel() {}
|
||||
|
||||
void AtcStationModel::updateAtcStations(const QVector<AtcStation> &atcStations)
|
||||
{
|
||||
// Add stations which didn't exist yet
|
||||
for (const auto &station : atcStations)
|
||||
{
|
||||
if (! m_atcStations.contains(station)) { addStation(station); }
|
||||
}
|
||||
|
||||
// Remove all stations which are no longer there
|
||||
for (int i = m_atcStations.size() - 1; i >= 0; i--)
|
||||
{
|
||||
AtcStation &station = m_atcStations[i];
|
||||
if (! m_atcStations.contains(station))
|
||||
{
|
||||
removeStationAtPosition(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtcStationModel::addStation(const AtcStation &atcStation)
|
||||
{
|
||||
beginInsertRows(QModelIndex(), rowCount(), rowCount());
|
||||
m_atcStations << atcStation;
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void AtcStationModel::removeStationAtPosition(int i)
|
||||
{
|
||||
beginRemoveRows(QModelIndex(), i, i);
|
||||
m_atcStations.removeAt(i);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
int AtcStationModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_atcStations.count();
|
||||
}
|
||||
|
||||
QVariant AtcStationModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= m_atcStations.count())
|
||||
return QVariant();
|
||||
|
||||
const AtcStation &atcStation = m_atcStations[index.row()];
|
||||
if (role == CallsignRole)
|
||||
return atcStation.callsign();
|
||||
else if (role == LatitudeRole)
|
||||
return atcStation.latitude();
|
||||
else if (role == LongitudeRole)
|
||||
return atcStation.longitude();
|
||||
else if (role == RadioDistanceRole)
|
||||
return atcStation.radioDistanceM();
|
||||
else if (role == FrequencyRole)
|
||||
return atcStation.formattedFrequency();
|
||||
else if (role == FrequencyKhzRole)
|
||||
return atcStation.frequency() / 1000;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> AtcStationModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[CallsignRole] = "callsign";
|
||||
roles[LatitudeRole] = "latitude";
|
||||
roles[LongitudeRole] = "longitude";
|
||||
roles[RadioDistanceRole] = "radioDistanceM";
|
||||
roles[FrequencyRole] = "frequencyAsString";
|
||||
roles[FrequencyKhzRole] = "frequencyKhz";
|
||||
return roles;
|
||||
}
|
||||
70
samples/afvclient/models/atcstationmodel.h
Normal file
70
samples/afvclient/models/atcstationmodel.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#ifndef ATCSTATIONMODEL_H
|
||||
#define ATCSTATIONMODEL_H
|
||||
|
||||
#include "dto.h"
|
||||
#include <QtGlobal>
|
||||
#include <QAbstractListModel>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
|
||||
class AtcStation
|
||||
{
|
||||
public:
|
||||
AtcStation() {}
|
||||
AtcStation(const QString &callsign, const TransceiverDto &transceiver);
|
||||
|
||||
QString callsign() const;
|
||||
double latitude() const;
|
||||
double longitude() const;
|
||||
quint32 frequency() const;
|
||||
|
||||
QString formattedFrequency() const;
|
||||
|
||||
double radioDistanceM() const;
|
||||
|
||||
private:
|
||||
QString m_callsign;
|
||||
TransceiverDto m_transceiver;
|
||||
};
|
||||
|
||||
inline bool operator==(const AtcStation& lhs, const AtcStation& rhs)
|
||||
{
|
||||
return lhs.callsign() == rhs.callsign() &&
|
||||
qFuzzyCompare(lhs.latitude(), rhs.latitude()) &&
|
||||
qFuzzyCompare(lhs.longitude(), rhs.longitude());
|
||||
}
|
||||
|
||||
|
||||
class AtcStationModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum AtcStationRoles {
|
||||
CallsignRole = Qt::UserRole + 1,
|
||||
LatitudeRole,
|
||||
LongitudeRole,
|
||||
RadioDistanceRole,
|
||||
FrequencyRole,
|
||||
FrequencyKhzRole
|
||||
};
|
||||
|
||||
AtcStationModel(QObject *parent = nullptr);
|
||||
virtual ~AtcStationModel();
|
||||
|
||||
void updateAtcStations(const QVector<AtcStation> &atcStations);
|
||||
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
protected:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
private:
|
||||
void addStation(const AtcStation &atcStation);
|
||||
void removeStationAtPosition(int i);
|
||||
|
||||
QList<AtcStation> m_atcStations;
|
||||
};
|
||||
|
||||
#endif // ATCSTATIONMODEL_H
|
||||
74
samples/afvclient/qml/AtcRing.qml
Normal file
74
samples/afvclient/qml/AtcRing.qml
Normal file
@@ -0,0 +1,74 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtLocation 5.12
|
||||
import QtPositioning 5.12
|
||||
|
||||
MapItemGroup {
|
||||
id: atcRing
|
||||
|
||||
signal selected(real latitude, real longitude, string frequency)
|
||||
|
||||
property alias position: mainCircle.center
|
||||
property alias radius: mainCircle.radius
|
||||
property alias cs: idCallsignText.text
|
||||
property alias freqAsString: idFrequency.text
|
||||
property int freqKhz: 122800
|
||||
|
||||
MapCircle {
|
||||
id: mainCircle
|
||||
color: 'green'
|
||||
border.width: 3
|
||||
border.color: 'green'
|
||||
opacity: 0.3
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
idCallsign.visible = !idCallsign.visible
|
||||
}
|
||||
onDoubleClicked: {
|
||||
atcRing.selected(mainCircle.center.latitude, mainCircle.center.longitude, atcRing.freqKhz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapQuickItem {
|
||||
id: circleCenter
|
||||
sourceItem: Rectangle { width: 6; height: 6; color: "#000000"; border.width: 2; border.color: "#000000"; smooth: true; radius: 3 }
|
||||
coordinate: mainCircle.center
|
||||
opacity:1.0
|
||||
anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
|
||||
}
|
||||
|
||||
MapQuickItem {
|
||||
id: idCallsign
|
||||
visible: false
|
||||
coordinate: mainCircle.center
|
||||
anchorPoint: Qt.point(-circleCenter.sourceItem.width * 0.5, circleCenter.sourceItem.height * -1.5)
|
||||
|
||||
sourceItem: Item {
|
||||
|
||||
Rectangle {
|
||||
color: "#FFFFFF"
|
||||
width: idCallsignText.width * 1.3
|
||||
height: (idCallsignText.height + idFrequency.height) * 1.3
|
||||
border.width: 2
|
||||
border.color: "#000000"
|
||||
radius: 5
|
||||
}
|
||||
|
||||
Text {
|
||||
id: idCallsignText
|
||||
color:"#000000"
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Text {
|
||||
id: idFrequency
|
||||
color:"#000000"
|
||||
anchors.top: idCallsignText.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
samples/afvclient/qml/Transceiver.qml
Normal file
74
samples/afvclient/qml/Transceiver.qml
Normal file
@@ -0,0 +1,74 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
|
||||
Row {
|
||||
property int transceiverId: 0
|
||||
property alias frequency: sbFrequency.value
|
||||
|
||||
spacing: 10
|
||||
Label {
|
||||
id: lblRadio
|
||||
text: 'Radio ' + transceiverId
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: sbFrequency
|
||||
width: 150
|
||||
height: 40
|
||||
editable: true
|
||||
stepSize: 25
|
||||
to: 140000
|
||||
from: 110000
|
||||
value: 122800
|
||||
|
||||
property int decimals: 3
|
||||
property real realValue: value / 1000
|
||||
|
||||
validator: DoubleValidator {
|
||||
bottom: Math.min(sbFrequency.from, sbFrequency.to)
|
||||
top: Math.max(sbFrequency.from, sbFrequency.to)
|
||||
}
|
||||
|
||||
textFromValue: function(value, locale) {
|
||||
return Number(value / 1000).toLocaleString(locale, 'f', sbFrequency.decimals)
|
||||
}
|
||||
|
||||
valueFromText: function(text, locale) {
|
||||
return Number.fromLocaleString(locale, text) * 1000
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onWheel: {
|
||||
if (wheel.angleDelta.y > 0)
|
||||
{
|
||||
sbFrequency.value += sbFrequency.stepSize
|
||||
}
|
||||
else
|
||||
{
|
||||
sbFrequency.value -= sbFrequency.stepSize
|
||||
}
|
||||
wheel.accepted=true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: cbTxOn
|
||||
height: 25
|
||||
text: qsTr("TX")
|
||||
checked: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: cbEnabled
|
||||
height: 25
|
||||
text: qsTr("Enabled")
|
||||
checked: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
336
samples/afvclient/qml/main.qml
Normal file
336
samples/afvclient/qml/main.qml
Normal file
@@ -0,0 +1,336 @@
|
||||
import QtQuick 2.12
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQuick.Controls 2.12
|
||||
import QtLocation 5.12
|
||||
import QtPositioning 5.12
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
width: 1200
|
||||
height: 520
|
||||
visible: true
|
||||
title: "Audio For Vatsim"
|
||||
|
||||
Plugin {
|
||||
id: mapPlugin
|
||||
name: "osm" // "mapboxgl", "esri", ...
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: leftGrid
|
||||
columns: 2
|
||||
rows: 6
|
||||
spacing: 10
|
||||
padding: 10
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 5
|
||||
anchors.leftMargin: 10
|
||||
|
||||
Label {
|
||||
id: lblUsername
|
||||
width: 100
|
||||
text: qsTr("Username")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: false
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: tfUsername
|
||||
width: 350
|
||||
height: 25
|
||||
text: qsTr("1234567")
|
||||
selectByMouse: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblPassword
|
||||
width: 100
|
||||
text: qsTr("Password")
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: false
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: tfPassword
|
||||
width: 350
|
||||
height: 25
|
||||
text: qsTr("123456")
|
||||
selectByMouse: true
|
||||
echoMode: TextInput.PasswordEchoOnEdit
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblCallsign
|
||||
width: 100
|
||||
text: qsTr("Callsign")
|
||||
Layout.fillWidth: false
|
||||
Layout.fillHeight: false
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: tfCallsign
|
||||
width: 350
|
||||
height: 25
|
||||
text: qsTr("DECHK")
|
||||
selectByMouse: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblInputDevice
|
||||
width: 100
|
||||
text: qsTr("Input Device")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: false
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cbInputDevices
|
||||
width: 350
|
||||
height: 25
|
||||
model: voiceClient.availableInputDevices()
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblOutputDevice
|
||||
width: 100
|
||||
text: qsTr("Output Device")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: false
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: cbOutputDevices
|
||||
width: 350
|
||||
height: 25
|
||||
model: voiceClient.availableOutputDevices()
|
||||
}
|
||||
|
||||
Frame {
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
border.color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: 10
|
||||
|
||||
Button {
|
||||
id: btConnect
|
||||
|
||||
property bool connected: false
|
||||
width: 170
|
||||
height: 25
|
||||
text: qsTr("Connect")
|
||||
onClicked: {
|
||||
if (btConnect.connected) {
|
||||
btConnect.connected = false;
|
||||
btConnect.text = qsTr("Connect")
|
||||
voiceClient.disconnectFrom()
|
||||
} else {
|
||||
btConnect.connected = true
|
||||
btConnect.text = qsTr("Disconnect")
|
||||
voiceClient.connectTo(tfUsername.text, tfPassword.text, tfCallsign.text)
|
||||
afvMapReader.setOwnCallsign(tfCallsign.text)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
id: btStartAudio
|
||||
|
||||
property bool started: false
|
||||
width: 170
|
||||
height: 25
|
||||
text: qsTr("Start Audio")
|
||||
onClicked: {
|
||||
btStartAudio.enabled = false
|
||||
cbInputDevices.enabled = false
|
||||
cbOutputDevices.enabled = false
|
||||
voiceClient.start(cbInputDevices.currentText, cbOutputDevices.currentText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Grid {
|
||||
id: rightGrid
|
||||
padding: 10
|
||||
anchors.top: parent.top
|
||||
anchors.left: leftGrid.right
|
||||
anchors.right: parent.right
|
||||
spacing: 10
|
||||
rows: 2
|
||||
columns: 3
|
||||
|
||||
Transceiver { id: transceiver1; transceiverId: 0 }
|
||||
|
||||
SpinBox {
|
||||
id: sbAltitude
|
||||
width: 150
|
||||
height: 40
|
||||
stepSize: 500
|
||||
to: 50000
|
||||
from: 0
|
||||
value: 1000
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblReceivingCom1
|
||||
height: 40
|
||||
text: qsTr("Receiving:")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Transceiver { id: transceiver2; transceiverId: 1 }
|
||||
|
||||
Button {
|
||||
id: btUpdateStack
|
||||
width: 150
|
||||
height: 40
|
||||
text: qsTr("Update Stack")
|
||||
onClicked: {
|
||||
voiceClient.updateComFrequency(0, transceiver1.frequency * 1000)
|
||||
voiceClient.updateComFrequency(1, transceiver2.frequency * 1000)
|
||||
voiceClient.updatePosition(map.center.latitude, map.center.longitude, sbAltitude.value)
|
||||
voiceClient.updateTransceivers()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: lblReceivingCom2
|
||||
height: 40
|
||||
text: qsTr("Receiving:")
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: column
|
||||
padding: 10
|
||||
spacing: 10
|
||||
anchors.top: rightGrid.bottom
|
||||
anchors.left: leftGrid.right
|
||||
anchors.right: parent.right
|
||||
|
||||
ProgressBar {
|
||||
id: pbAudioInput
|
||||
width: 500
|
||||
height: 25
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
value: voiceClient.inputVolumePeakVU
|
||||
}
|
||||
|
||||
ProgressBar {
|
||||
id: pbAudioOutput
|
||||
width: 500
|
||||
height: 25
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 10
|
||||
value: voiceClient.outputVolumePeakVU
|
||||
}
|
||||
}
|
||||
|
||||
//// CheckBox {
|
||||
//// id: cbVhfEffects
|
||||
//// anchors.topMargin: 5
|
||||
//// anchors.leftMargin: 10
|
||||
//// anchors.left: sbAltitude.right
|
||||
//// anchors.top: parent.top
|
||||
//// anchors.verticalCenter: sbAltitude.verticalCenter
|
||||
//// height: 25
|
||||
//// text: qsTr("VHF Effects")
|
||||
//// checked: true
|
||||
//// }
|
||||
|
||||
Map {
|
||||
id: map
|
||||
anchors.topMargin: 5
|
||||
anchors.top: leftGrid.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
plugin: mapPlugin
|
||||
center: QtPositioning.coordinate(48.50, 11.50) // Oslo
|
||||
zoomLevel: 3
|
||||
|
||||
// MapCircle {
|
||||
// center {
|
||||
// latitude: map.center.latitude
|
||||
// longitude: map.center.longitude
|
||||
// }
|
||||
// radius: 500000.0
|
||||
// color: 'blue'
|
||||
// border.width: 3
|
||||
// border.color: 'blue'
|
||||
// opacity: 0.3
|
||||
// }
|
||||
|
||||
MapItemView {
|
||||
model: afvMapReader.atcStationModel
|
||||
delegate: atcDelegate
|
||||
}
|
||||
|
||||
Component {
|
||||
id: atcDelegate
|
||||
AtcRing {
|
||||
position {
|
||||
latitude: latitude
|
||||
longitude: longitude
|
||||
}
|
||||
radius: radioDistanceM
|
||||
cs: callsign
|
||||
freqAsString: frequencyAsString
|
||||
freqKhz: frequencyKhz
|
||||
onSelected: {
|
||||
map.center = QtPositioning.coordinate(latitude, longitude)
|
||||
transceiver1.frequency = frequency
|
||||
voiceClient.updateComFrequency(0, transceiver1.frequency * 1000)
|
||||
voiceClient.updateComFrequency(1, transceiver2.frequency * 1000)
|
||||
voiceClient.updatePosition(map.center.latitude, map.center.longitude, sbAltitude.value)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 3
|
||||
height: 15
|
||||
color: "blue"
|
||||
anchors.verticalCenter: map.verticalCenter
|
||||
anchors.horizontalCenter: map.horizontalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 15
|
||||
height: 3
|
||||
color: "blue"
|
||||
anchors.verticalCenter: map.verticalCenter
|
||||
anchors.horizontalCenter: map.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 5000; running: true; repeat: true
|
||||
onTriggered: {
|
||||
voiceClient.updateComFrequency(0, transceiver1.frequency * 1000)
|
||||
voiceClient.updateComFrequency(1, transceiver2.frequency * 1000)
|
||||
voiceClient.updatePosition(map.center.latitude, map.center.longitude, sbAltitude.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
7
samples/afvclient/qml/qml.qrc
Normal file
7
samples/afvclient/qml/qml.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
<file>AtcRing.qml</file>
|
||||
<file>Transceiver.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -11,6 +11,7 @@ SUBDIRS += sampleblackmiscsim
|
||||
SUBDIRS += samplehotkey
|
||||
SUBDIRS += sampleweatherdata
|
||||
SUBDIRS += samplefsd
|
||||
SUBDIRS += afvclient
|
||||
|
||||
samplecliclient.file = cliclient/samplecliclient.pro
|
||||
sampleblackmiscquantities.file = blackmiscquantities/sampleblackmiscquantities.pro
|
||||
|
||||
Reference in New Issue
Block a user