From b30527395fcc7765e07068a25156aba3945dccbf Mon Sep 17 00:00:00 2001 From: Jie Feng Date: Wed, 2 Oct 2024 00:18:40 +0800 Subject: [PATCH 1/2] Fix desktop build --- .gitignore | 3 +++ LogHandler.cpp | 2 ++ dmr.cpp | 2 +- dmr.h | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4aa27fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +android/ +android-build/ \ No newline at end of file diff --git a/LogHandler.cpp b/LogHandler.cpp index 6632a0f..e504122 100644 --- a/LogHandler.cpp +++ b/LogHandler.cpp @@ -234,6 +234,7 @@ void LogHandler::shareFile(const QString &filePath) { #endif } void LogHandler::shareFileDirectly(const QString &filePath) { +#ifdef Q_OS_ANDROID QJniObject context = QNativeInterface::QAndroidApplication::context(); if (context.isValid()) { @@ -274,5 +275,6 @@ void LogHandler::shareFileDirectly(const QString &filePath) { } else { qWarning("Invalid context or file path object."); } +#endif } diff --git a/dmr.cpp b/dmr.cpp index 385e091..177f93f 100644 --- a/dmr.cpp +++ b/dmr.cpp @@ -20,7 +20,7 @@ #include #include "dmr.h" #include -#include "DroidStar.h" +#include "droidstar.h" #include #include diff --git a/dmr.h b/dmr.h index 61eeaa7..7c4caa2 100644 --- a/dmr.h +++ b/dmr.h @@ -29,7 +29,7 @@ #include #include #include -#include "SignalEmitter.h" +//#include "SignalEmitter.h" #include From 4c83f05ed2a71de5b1082d0acf294a3781bbe40b Mon Sep 17 00:00:00 2001 From: Jie Feng Date: Wed, 2 Oct 2024 00:43:42 +0800 Subject: [PATCH 2/2] Add ASL Web Transceiver support --- MainTab.qml | 1 + README.md | 1 + SettingsTab.qml | 36 ++++++++++++++----- droidstar.cpp | 57 ++++++++++++++++++++++++++++- droidstar.h | 6 ++++ iax.cpp | 95 +++++++++++++++++++++++++++++++++++-------------- iax.h | 5 ++- main.qml | 1 + mode.h | 2 +- 9 files changed, 166 insertions(+), 38 deletions(-) diff --git a/MainTab.qml b/MainTab.qml index a317404..ac27af4 100644 --- a/MainTab.qml +++ b/MainTab.qml @@ -430,6 +430,7 @@ contentItem: Text { droidstar.set_essid(settingsTab.comboEssid.currentText); droidstar.set_bm_password(settingsTab.bmpwEdit.text); droidstar.set_tgif_password(settingsTab.tgifpwEdit.text); + droidstar.set_asl_password(settingsTab.aslpwEdit.text); droidstar.set_latitude(settingsTab.latEdit.text); droidstar.set_longitude(settingsTab.lonEdit.text); droidstar.set_location(settingsTab.locEdit.text); diff --git a/README.md b/README.md index 2d55f79..64dd16c 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ I am excited to announce the release of several new features and improvements: - **Recent TG-ID Dropdown**: The dropdown records all the recently dialed Talk Group ids and lets you choose from the list. You no longer have to memorize the TG ID. +- **ASL Web Transceiver**: Allows you to connect to any ASL node with your ASL password. ## Downloads diff --git a/SettingsTab.qml b/SettingsTab.qml index 7dbc79b..ed9f544 100644 --- a/SettingsTab.qml +++ b/SettingsTab.qml @@ -26,6 +26,7 @@ Item { property alias comboEssid: comboessid property alias bmpwEdit: bmpwedit property alias tgifpwEdit: tgifpwedit + property alias aslpwEdit: aslpwedit property alias latEdit: latedit property alias lonEdit: lonedit property alias locEdit: locedit @@ -259,11 +260,30 @@ Item { echoMode: TextInput.Password } Text { - id: latLabel + id: aslpwLabel x: 10 y: 270 width: 80 height: 25 + text: qsTr("ASL Pass") + color: "white" + verticalAlignment: Text.AlignVCenter + } + TextField { + id: aslpwedit + x: 100 + y: aslpwLabel.y + width: parent.width - 110 + height: 25 + selectByMouse: true + echoMode: TextInput.Password + } + Text { + id: latLabel + x: 10 + y: 300 + width: 80 + height: 25 text: qsTr("Latitude") color: "white" verticalAlignment: Text.AlignVCenter @@ -279,7 +299,7 @@ Item { Text { id: lonLabel x: 10 - y: 300 + y: 330 width: 80 height: 25 text: qsTr("Longitude") @@ -297,7 +317,7 @@ Item { Text { id: locLabel x: 10 - y: 330 + y: 360 width: 80 height: 25 text: qsTr("Location") @@ -315,7 +335,7 @@ Item { Text { id: descLabel x: 10 - y: 360 + y: 390 width: 80 height: 25 text: qsTr("Description") @@ -333,7 +353,7 @@ Item { Text { id: urlLabel x: 10 - y: 390 + y: 420 width: 80 height: 25 text: qsTr("URL") @@ -351,7 +371,7 @@ Item { Text { id: swidLabel x: 10 - y: 420 + y: 450 width: 80 height: 25 text: qsTr("SoftwareID") @@ -369,7 +389,7 @@ Item { Text { id: pkgidLabel x: 10 - y: 450 + y: 480 width: 80 height: 25 text: qsTr("PackageID") @@ -387,7 +407,7 @@ Item { Text { id: dmroptslabel x: 10 - y: 480 + y: 510 width: 80 height: 25 text: qsTr("DMR+ Opts") diff --git a/droidstar.cpp b/droidstar.cpp index 39b7ecb..9a13844 100644 --- a/droidstar.cpp +++ b/droidstar.cpp @@ -358,6 +358,56 @@ void DroidStar::tts_text_changed(QString ttstxt) emit input_source_changed(m_tts, m_ttstxt); } + +void DroidStar::obtain_asl_wt_creds() +{ + if (!m_wt_callingname.isEmpty() && (m_asl_password == m_wt_callingname_pass)) { + return; + } + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + QUrl url("https://www.allstarlink.org/portal/login.php"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + + QByteArray postData; + postData.append("user=" + QUrl::toPercentEncoding(m_callsign.toUtf8())); + postData.append("&pass=" + QUrl::toPercentEncoding(m_asl_password.toUtf8())); + + connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply) { + if (reply->error() == QNetworkReply::NoError) { + QUrl url("https://www.allstarlink.org/portal/webtransceiver.php?node=12345"); + QNetworkRequest request(url); + + connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply) { + if (reply->error() == QNetworkReply::NoError) { + QString html = reply->readAll(); + QStringList l = html.split('\n'); + for (int i = 0; i < l.size(); i++) { + if (l.at(i).contains("callingName")) { + QStringList ll = l.at(i).split('"'); + m_wt_callingname = ll.at(3); + m_wt_callingname_pass = m_asl_password; + break; + } + } + } else { + // Handle error + qDebug() << "Error: " << reply->errorString(); + } + reply->deleteLater(); + }); + + manager->get(request); + } else { + // Handle error + qDebug() << "Error: " << reply->errorString(); + } + reply->deleteLater(); + }); + + manager->post(request, postData); +} + void DroidStar::process_connect() { if(connect_status != Mode::DISCONNECTED){ @@ -465,7 +515,7 @@ void DroidStar::process_connect() if(m_protocol == "IAX"){ QString iaxuser = sl.at(2).simplified(); QString iaxpass = sl.at(3).simplified(); - m_mode->set_iax_params(iaxuser, iaxpass, m_refname, m_host, m_port); + m_mode->set_iax_params(iaxuser, iaxpass, m_wt_callingname, m_refname, m_host, m_port); connect(this, SIGNAL(send_dtmf(QByteArray)), m_mode, SLOT(send_dtmf(QByteArray))); } @@ -652,6 +702,9 @@ void DroidStar::process_mode_change(const QString &m) } if(m == "IAX"){ process_iax_hosts(); + if (!m_asl_password.isEmpty()) { + obtain_asl_wt_creds(); + } m_label1 = ""; m_label2 = ""; m_label3 = ""; @@ -684,6 +737,7 @@ void DroidStar::save_settings() m_settings->setValue("ESSID", m_essid); m_settings->setValue("BMPASSWORD", m_bm_password); m_settings->setValue("TGIFPASSWORD", m_tgif_password); + m_settings->setValue("ASLPASSWORD", m_asl_password); m_settings->setValue("DMRTGID", m_dmr_destid); m_settings->setValue("DMRLAT", m_latitude); m_settings->setValue("DMRLONG", m_longitude); @@ -746,6 +800,7 @@ void DroidStar::process_settings() m_essid = m_settings->value("ESSID").toString().simplified().toUInt(); m_bm_password = m_settings->value("BMPASSWORD").toString().simplified(); m_tgif_password = m_settings->value("TGIFPASSWORD").toString().simplified(); + m_asl_password = m_settings->value("ASLPASSWORD").toString().simplified(); m_latitude = m_settings->value("DMRLAT", "0").toString().simplified(); m_longitude = m_settings->value("DMRLONG", "0").toString().simplified(); m_location = m_settings->value("DMRLOC").toString().simplified(); diff --git a/droidstar.h b/droidstar.h index 49a0380..db875e6 100644 --- a/droidstar.h +++ b/droidstar.h @@ -113,6 +113,7 @@ public slots: } void set_bm_password(const QString &bmpwd) { m_bm_password = bmpwd; save_settings(); } void set_tgif_password(const QString &tgifpwd) { m_tgif_password = tgifpwd; save_settings(); } + void set_asl_password(const QString &aslpwd) { m_asl_password = aslpwd; save_settings(); } void set_latitude(const QString &lat){ m_latitude = lat; save_settings(); } void set_longitude(const QString &lon){ m_longitude = lon; save_settings(); } void set_location(const QString &loc){ m_location = loc; save_settings(); } @@ -206,6 +207,7 @@ public slots: QString get_essid() { return m_essid ? QString("%1").arg(m_essid - 1, 2, 10, QChar('0')) : "None"; } QString get_bm_password() { return m_bm_password; } QString get_tgif_password() { return m_tgif_password; } + QString get_asl_password() { return m_asl_password; } QString get_latitude() { return m_latitude; } QString get_longitude() { return m_longitude; } QString get_location() { return m_location; } @@ -283,6 +285,7 @@ public slots: void set_output_level(unsigned short l){ m_outlevel = l; } void tts_changed(QString); void tts_text_changed(QString); + void obtain_asl_wt_creds(); private: int connect_status; bool m_update_host_files; @@ -298,6 +301,7 @@ private: QString m_protocol; QString m_bm_password; QString m_tgif_password; + QString m_asl_password; QString m_latitude; QString m_longitude; QString m_location; @@ -372,6 +376,8 @@ private: QStringList m_playbacks; QStringList m_captures; bool m_mdirect; + QString m_wt_callingname; + QString m_wt_callingname_pass; int m_tts; QString m_ttstxt; diff --git a/iax.cpp b/iax.cpp index 56bcc3f..ef38acc 100644 --- a/iax.cpp +++ b/iax.cpp @@ -48,7 +48,8 @@ IAX::IAX() : m_rxdropped(0), m_rxooo(0), m_ttsid(0), - m_cnt(0) + m_cnt(0), + m_wt(false) { #ifdef USE_FLITE flite_init(); @@ -62,13 +63,14 @@ IAX::~IAX() { } -void IAX::set_iax_params(QString username, QString password, QString node, QString host, int port) +void IAX::set_iax_params(QString username, QString password, QString callingname, QString node, QString host, int port) { m_username = username; m_password = password; m_node = node; m_host = host; m_port = port; + m_callingname = callingname; QStringList l = m_node.split('@'); if(l.size() == 2){ @@ -78,6 +80,10 @@ void IAX::set_iax_params(QString username, QString password, QString node, QStri else{ m_context = "iax-client"; } + + if (m_username == "allstar-public" && m_password == "allstar") { + m_wt = true; + } } int16_t ulaw_decode(int8_t number) @@ -123,6 +129,9 @@ int8_t ulaw_encode(int16_t number) void IAX::send_call() { + if (m_wt) { + //send_disconnect(); + } uint16_t scall = htons(++m_scallno | 0x8000); m_oseq = m_iseq = 0; QByteArray out; @@ -142,13 +151,28 @@ void IAX::send_call() out.append('\x00'); out.append(IAX_PROTO_VERSION); out.append(IAX_IE_CALLED_NUMBER); - out.append(m_node.size()); - out.append(m_node.toUtf8(), m_node.size()); + if (m_wt) { + out.append('\x01'); + out.append('s'); + } else { + out.append(m_node.size()); + out.append(m_node.toUtf8(), m_node.size()); + } out.append(IAX_IE_CALLING_NUMBER); - out.append('\x00'); + if (m_wt) { + out.append(m_node.size()); + out.append(m_node.toUtf8(), m_node.size()); + } else { + out.append('\x00'); + } out.append(IAX_IE_CALLING_NAME); - out.append(m_callsign.size()); - out.append(m_callsign.toUtf8(), m_callsign.size()); + if (m_wt) { + out.append(m_callingname.size()); + out.append(m_callingname.toUtf8(), m_callingname.size()); + } else { + out.append(m_callsign.size()); + out.append(m_callsign.toUtf8(), m_callsign.size()); + } out.append(IAX_IE_USERNAME); out.append(m_username.size()); out.append(m_username.toUtf8(), m_username.size()); @@ -499,10 +523,16 @@ void IAX::hostname_lookup(QHostInfo i) m_udp = new QUdpSocket(this); m_regtimer = new QTimer(); connect(m_udp, SIGNAL(readyRead()), this, SLOT(process_udp())); - connect(m_regtimer, SIGNAL(timeout()), this, SLOT(send_registration())); + if (m_wt) { + connect(m_regtimer, SIGNAL(timeout()), this, SLOT(send_call())); + send_call(); + //m_regtimer->start(240000); + } else { + connect(m_regtimer, SIGNAL(timeout()), this, SLOT(send_registration())); + send_registration(0); + m_regtimer->start(60000); + } m_timestamp = QDateTime::currentMSecsSinceEpoch(); - send_registration(0); - m_regtimer->start(60000); } } @@ -608,6 +638,9 @@ void IAX::process_udp() { //int16_t zeropcm[160]; //memset(zeropcm, 0, 160 * sizeof(int16_t)); + if (m_wt && m_modeinfo.status == CONNECTING) { + connected(); + } ++m_rxframes; m_dcallno = (((buf.data()[0] & 0x7f) << 8) | ((uint8_t)buf.data()[1])); m_iseq = buf.data()[8] + 1; @@ -620,22 +653,7 @@ void IAX::process_udp() (buf.data()[11] == AST_CONTROL_ANSWER) ) { if(m_modeinfo.status == CONNECTING){ - m_modeinfo.status = CONNECTED_RW; - m_txtimer = new QTimer(); - connect(m_txtimer, SIGNAL(timeout()), this, SLOT(transmit())); - m_rxtimer = new QTimer(); - connect(m_rxtimer, SIGNAL(timeout()), this, SLOT(process_rx_data())); - m_rxtimer->start(19); - m_pingtimer = new QTimer(); - connect(m_pingtimer, SIGNAL(timeout()), this, SLOT(send_ping())); - m_pingtimer->start(2000); - m_audio = new AudioEngine(m_audioin, m_audioout); - m_audio->init(); - m_audio->start_playback(); - m_audio->set_input_buffer_size(640); - m_audio->start_capture(); - //m_txtimer->start(19); - m_modeinfo.sw_vocoder_loaded = true; + connected(); } ++m_rxframes; m_dcallno = (((buf.data()[0] & 0x7f) << 8) | ((uint8_t)buf.data()[1])); @@ -735,6 +753,25 @@ void IAX::process_udp() emit update(m_modeinfo); } +void IAX::connected() { + m_modeinfo.status = CONNECTED_RW; + m_txtimer = new QTimer(); + connect(m_txtimer, SIGNAL(timeout()), this, SLOT(transmit())); + m_rxtimer = new QTimer(); + connect(m_rxtimer, SIGNAL(timeout()), this, SLOT(process_rx_data())); + m_rxtimer->start(19); + m_pingtimer = new QTimer(); + connect(m_pingtimer, SIGNAL(timeout()), this, SLOT(send_ping())); + m_pingtimer->start(2000); + m_audio = new AudioEngine(m_audioin, m_audioout); + m_audio->init(); + m_audio->start_playback(); + m_audio->set_input_buffer_size(640); + m_audio->start_capture(); + //m_txtimer->start(19); + m_modeinfo.sw_vocoder_loaded = true; +} + void IAX::process_rx_data() { int16_t pcm[160]; @@ -757,12 +794,14 @@ void IAX::toggle_tx(bool tx) void IAX::start_tx() { + int16_t pcm[160]; //std::cerr << "Pressed TX buffersize == " << audioin->bufferSize() << std::endl; //QByteArray tx("*99", 3); //send_dtmf(tx); send_radio_key(true); m_ttscnt = 0; qDebug() << "start_tx() " << m_ttsid << " " << m_ttstext; + while(m_audio->read(pcm)); m_tx = true; #ifdef USE_FLITE if(m_ttsid == 1){ @@ -820,7 +859,9 @@ void IAX::transmit() for(int i = 0; i < s; ++i){ out.append(ulaw_encode(pcm[i])); } - m_udp->writeDatagram(out, m_address, m_port); + if (!m_wt || m_tx) { + m_udp->writeDatagram(out, m_address, m_port); + } #ifdef DEBUGG fprintf(stderr, "SEND: "); for(int i = 0; i < out.size(); ++i){ diff --git a/iax.h b/iax.h index 60c9cda..12b0039 100644 --- a/iax.h +++ b/iax.h @@ -26,7 +26,7 @@ class IAX : public Mode public: IAX();//QString callsign, QString username, QString password, QString node, QString host, int port, QString audioin, QString audioout); ~IAX(); - void set_iax_params(QString username, QString password, QString node, QString host, int port); + void set_iax_params(QString username, QString password, QString callingname, QString node, QString host, int port); //uint8_t get_status(){ return m_status; } QString get_host() { return m_host; } int get_port() { return m_port; } @@ -54,12 +54,14 @@ private slots: void send_radio_key(bool); void in_audio_vol_changed(qreal v){ m_audio->set_input_volume(v); } void out_audio_vol_changed(qreal v){ m_audio->set_output_volume(v); } + void connected(); private: QUdpSocket *m_udp = nullptr; QHostAddress m_address; QString m_callsign; QString m_username; QString m_password; + QString m_callingname; QString m_node; QString m_context; QString m_host; @@ -93,6 +95,7 @@ private: QString m_ttstext; uint16_t m_ttscnt; int m_cnt; + bool m_wt; //qreal m_rxgain; #ifdef USE_FLITE cst_voice *voice_slt; diff --git a/main.qml b/main.qml index ae196b4..18ca8ad 100644 --- a/main.qml +++ b/main.qml @@ -530,6 +530,7 @@ ApplicationWindow { settingsTab.comboEssid.currentIndex = settingsTab.comboEssid.find(droidstar.get_essid()); settingsTab.bmpwEdit.text = droidstar.get_bm_password(); settingsTab.tgifpwEdit.text = droidstar.get_tgif_password(); + settingsTab.aslpwEdit.text = droidstar.get_asl_password(); settingsTab.latEdit.text = droidstar.get_latitude(); settingsTab.lonEdit.text = droidstar.get_longitude(); settingsTab.locEdit.text = droidstar.get_location(); diff --git a/mode.h b/mode.h index cdd5a69..dd72f48 100644 --- a/mode.h +++ b/mode.h @@ -70,7 +70,7 @@ public: m_m17TXLevel = m17TXLevel; } virtual void set_dmr_params(uint8_t, QString, QString, QString, QString, QString, QString, QString, QString, QString, QString) {} - virtual void set_iax_params(QString, QString, QString, QString, int) {} + virtual void set_iax_params(QString, QString, QString, QString, QString, int) {} bool get_hwrx() { return m_hwrx; } bool get_hwtx() { return m_hwtx; } void set_hostname(std::string);