Merge branch 'f4exb:master' into freq_scanner

This commit is contained in:
srcejon 2024-04-02 13:52:10 +01:00 committed by GitHub
commit 625513eaeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 477 additions and 172 deletions

View File

@ -934,4 +934,13 @@ bool Packing::packfree(int a77[], const std::string& msg)
return true; return true;
} }
void Packing::pack1(int a77[], int c28_1, int c28_2, int g15, int report)
{
pa64(a77, 0, 28, c28_1);
pa64(a77, 28+1, 28, c28_2);
a77[28+1+28+1] = report;
pa64(a77, 28+1+28+2, 15, g15);
pa64(a77, 28+1+28+2+15, 3, 1);
}
} // namespace FT8 } // namespace FT8

View File

@ -40,6 +40,7 @@ public:
static bool packcall_std(int& c28, const std::string& callstr); static bool packcall_std(int& c28, const std::string& callstr);
static bool packgrid(int& g15, const std::string& locstr); static bool packgrid(int& g15, const std::string& locstr);
static bool packfree(int a77[], const std::string& msg); static bool packfree(int a77[], const std::string& msg);
static void pack1(int a77[], int c28_1, int c28_2, int g15, int report);
private: private:
static int ihashcall(std::string call, int m); static int ihashcall(std::string call, int m);

View File

@ -1,5 +1,10 @@
project(modchirpchat) project(modchirpchat)
if (FT8_SUPPORT)
set(chirpchatmod_FT8_LIB ft8)
set(chirpchatmod_FT8_INCLUDE ${CMAKE_SOURCE_DIR}/ft8)
endif()
set(modchirpchat_SOURCES set(modchirpchat_SOURCES
chirpchatmod.cpp chirpchatmod.cpp
chirpchatmodsettings.cpp chirpchatmodsettings.cpp
@ -10,6 +15,7 @@ set(modchirpchat_SOURCES
chirpchatmodencodertty.cpp chirpchatmodencodertty.cpp
chirpchatmodencoderascii.cpp chirpchatmodencoderascii.cpp
chirpchatmodencoderlora.cpp chirpchatmodencoderlora.cpp
chirpchatmodencoderft.cpp
chirpchatmodwebapiadapter.cpp chirpchatmodwebapiadapter.cpp
) )
@ -23,11 +29,13 @@ set(modchirpchat_HEADERS
chirpchatmodencodertty.h chirpchatmodencodertty.h
chirpchatmodencoderascii.h chirpchatmodencoderascii.h
chirpchatmodencoderlora.h chirpchatmodencoderlora.h
chirpchatmodencoderft.h
chirpchatmodwebapiadapter.h chirpchatmodwebapiadapter.h
) )
include_directories( include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${chirpchatmod_FT8_INCLUDE}
) )
if(NOT SERVER_MODE) if(NOT SERVER_MODE)
@ -38,7 +46,7 @@ if(NOT SERVER_MODE)
) )
set(modchirpchat_HEADERS set(modchirpchat_HEADERS
${modchirpchat_HEADERS} ${modchirpchat_HEADERS}
chirpchatmodgui.h chirpchatmodgui.h
) )
set(TARGET_NAME modchirpchat) set(TARGET_NAME modchirpchat)
@ -62,6 +70,7 @@ target_link_libraries(${TARGET_NAME}
sdrbase sdrbase
${TARGET_LIB_GUI} ${TARGET_LIB_GUI}
swagger swagger
${chirpchatmod_FT8_LIB}
) )
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -284,74 +284,19 @@ void ChirpChatMod::applySettings(const ChirpChatModSettings& settings, bool forc
{ {
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(); payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create();
} }
else if ((settings.m_messageType == ChirpChatModSettings::MessageBeacon) else if ((settings.m_messageType != m_settings.m_messageType)
&& ((settings.m_messageType != m_settings.m_messageType) || (settings.m_beaconMessage != m_settings.m_beaconMessage)
|| (settings.m_beaconMessage != m_settings.m_beaconMessage) || force)) || (settings.m_cqMessage != m_settings.m_cqMessage)
|| (settings.m_replyMessage != m_settings.m_replyMessage)
|| (settings.m_reportMessage != m_settings.m_reportMessage)
|| (settings.m_replyReportMessage != m_settings.m_replyReportMessage)
|| (settings.m_rrrMessage != m_settings.m_rrrMessage)
|| (settings.m_73Message != m_settings.m_73Message)
|| (settings.m_qsoTextMessage != m_settings.m_qsoTextMessage)
|| (settings.m_textMessage != m_settings.m_textMessage)
|| (settings.m_bytesMessage != m_settings.m_bytesMessage) || force)
{ {
m_encoder.encodeString(settings.m_beaconMessage, symbols); m_encoder.encode(settings, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageCQ)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_cqMessage != m_settings.m_cqMessage) || force))
{
m_encoder.encodeString(settings.m_cqMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageReply)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_replyMessage != m_settings.m_replyMessage) || force))
{
m_encoder.encodeString(settings.m_replyMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageReport)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_reportMessage != m_settings.m_reportMessage) || force))
{
m_encoder.encodeString(settings.m_reportMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageReplyReport)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_replyReportMessage != m_settings.m_replyReportMessage) || force))
{
m_encoder.encodeString(settings.m_replyReportMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageRRR)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_rrrMessage != m_settings.m_rrrMessage) || force))
{
m_encoder.encodeString(settings.m_rrrMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::Message73)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_73Message != m_settings.m_73Message) || force))
{
m_encoder.encodeString(settings.m_73Message, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageQSOText)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_qsoTextMessage != m_settings.m_qsoTextMessage) || force))
{
m_encoder.encodeString(settings.m_qsoTextMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageText)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_textMessage != m_settings.m_textMessage) || force))
{
m_encoder.encodeString(settings.m_textMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
}
else if ((settings.m_messageType == ChirpChatModSettings::MessageBytes)
&& ((settings.m_messageType != m_settings.m_messageType)
|| (settings.m_bytesMessage != m_settings.m_bytesMessage) || force))
{
m_encoder.encodeBytes(settings.m_bytesMessage, symbols);
payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols); payloadMsg = ChirpChatModBaseband::MsgConfigureChirpChatModPayload::create(symbols);
} }

View File

@ -19,6 +19,7 @@
#include "chirpchatmodencodertty.h" #include "chirpchatmodencodertty.h"
#include "chirpchatmodencoderascii.h" #include "chirpchatmodencoderascii.h"
#include "chirpchatmodencoderlora.h" #include "chirpchatmodencoderlora.h"
#include "chirpchatmodencoderft.h"
ChirpChatModEncoder::ChirpChatModEncoder() : ChirpChatModEncoder::ChirpChatModEncoder() :
m_codingScheme(ChirpChatModSettings::CodingTTY), m_codingScheme(ChirpChatModSettings::CodingTTY),
@ -44,6 +45,47 @@ void ChirpChatModEncoder::setNbSymbolBits(unsigned int spreadFactor, unsigned in
m_nbSymbolBits = m_spreadFactor - m_deBits; m_nbSymbolBits = m_spreadFactor - m_deBits;
} }
void ChirpChatModEncoder::encode(ChirpChatModSettings settings, std::vector<unsigned short>& symbols)
{
if (settings.m_codingScheme == ChirpChatModSettings::CodingFT)
{
ChirpChatModEncoderFT::encodeMsg(
settings.m_myCall,
settings.m_urCall,
settings.m_myLoc,
settings.m_myRpt,
settings.m_textMessage,
settings.m_messageType,
m_nbSymbolBits,
symbols
);
}
else
{
if (settings.m_messageType == ChirpChatModSettings::MessageBytes) {
encodeBytes(settings.m_bytesMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageBeacon) {
encodeString(settings.m_beaconMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageCQ) {
encodeString(settings.m_cqMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageReply) {
encodeString(settings.m_replyMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageReport) {
encodeString(settings.m_reportMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageReplyReport) {
encodeString(settings.m_replyReportMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageRRR) {
encodeString(settings.m_rrrMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::Message73) {
encodeString(settings.m_73Message, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageQSOText) {
encodeString(settings.m_qsoTextMessage, symbols);
} else if (settings.m_messageType == ChirpChatModSettings::MessageText) {
encodeString(settings.m_textMessage, symbols);
}
}
}
void ChirpChatModEncoder::encodeString(const QString& str, std::vector<unsigned short>& symbols) void ChirpChatModEncoder::encodeString(const QString& str, std::vector<unsigned short>& symbols)
{ {
switch (m_codingScheme) switch (m_codingScheme)

View File

@ -34,10 +34,11 @@ public:
void setLoRaParityBits(unsigned int parityBits) { m_nbParityBits = parityBits; } void setLoRaParityBits(unsigned int parityBits) { m_nbParityBits = parityBits; }
void setLoRaHasHeader(bool hasHeader) { m_hasHeader = hasHeader; } void setLoRaHasHeader(bool hasHeader) { m_hasHeader = hasHeader; }
void setLoRaHasCRC(bool hasCRC) { m_hasCRC = hasCRC; } void setLoRaHasCRC(bool hasCRC) { m_hasCRC = hasCRC; }
void encodeString(const QString& str, std::vector<unsigned short>& symbols);
void encodeBytes(const QByteArray& bytes, std::vector<unsigned short>& symbols); void encodeBytes(const QByteArray& bytes, std::vector<unsigned short>& symbols);
void encode(ChirpChatModSettings settings, std::vector<unsigned short>& symbols);
private: private:
void encodeString(const QString& str, std::vector<unsigned short>& symbols);
// LoRa functions // LoRa functions
void encodeBytesLoRa(const QByteArray& bytes, std::vector<unsigned short>& symbols); void encodeBytesLoRa(const QByteArray& bytes, std::vector<unsigned short>& symbols);

View File

@ -0,0 +1,220 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 as 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 V3 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 "chirpchatmodencoderft.h"
#ifndef HAS_FT8
void ChirpChatModEncoderFT::encodeMsg(
const QString& myCall,
const QString& urCall,
const QString& myLocator,
const QString& myReport,
const QString& textMessage,
ChirpChatModSettings::MessageType messageType,
unsigned int nbSymbolBits,
std::vector<unsigned short>& symbols
)
{
qDebug("ChirpChatModEncoderFT::encodeMsg: not implemented");
}
#else
#include "ft8.h"
#include "packing.h"
void ChirpChatModEncoderFT::encodeMsg(
const QString& myCall,
const QString& urCall,
const QString& myLocator,
const QString& myReport,
const QString& textMessage,
ChirpChatModSettings::MessageType messageType,
unsigned int nbSymbolBits,
std::vector<unsigned short>& symbols
)
{
int a174[174]; // FT payload is 174 bits
if (messageType == ChirpChatModSettings::MessageNone) {
return; // do nothing
} else if (messageType == ChirpChatModSettings::MessageBeacon) {
encodeMsgBeaconOrCQ(myCall, myLocator, "DE", a174);
} else if (messageType == ChirpChatModSettings::MessageCQ) {
encodeMsgBeaconOrCQ(myCall, myLocator, "CQ", a174);
} else if (messageType == ChirpChatModSettings::MessageReply) {
encodeMsgReply(myCall, urCall, myLocator, a174);
} else if (messageType == ChirpChatModSettings::MessageReport) {
encodeMsgReport(myCall, urCall, myReport, 0, a174);
} else if (messageType == ChirpChatModSettings::MessageReplyReport) {
encodeMsgReport(myCall, urCall, myReport, 1, a174);
} else if (messageType == ChirpChatModSettings::MessageRRR) {
encodeMsgReport(myCall, urCall, "RRR", 1, a174);
} else if (messageType == ChirpChatModSettings::Message73) {
encodeMsgReport(myCall, urCall, "73", 1, a174);
} else {
encodeTextMsg(textMessage, a174);
}
int allBits = ((174 / nbSymbolBits) + (174 % nbSymbolBits == 0 ? 0 : 1))*nbSymbolBits; // ensures zero bits padding
int iBit;
int symbol = 0;
for (int i = 0; i < allBits; i++)
{
iBit = nbSymbolBits - (i % nbSymbolBits) - 1; // MSB first
if (i < 174) {
symbol += a174[i] * (1<<iBit);
}
if ((i % nbSymbolBits) == (nbSymbolBits - 1))
{
symbols.push_back(symbol);
symbol = 0;
}
}
}
void ChirpChatModEncoderFT::encodeTextMsg(const QString& text, int a174[])
{
int a77[77];
std::fill(a77, a77 + 77, 0);
QString sentMsg = text.rightJustified(13, ' ', true);
if (!FT8::Packing::packfree(a77, sentMsg.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeTextMsg: failed to encode free text message (%s)", qPrintable(sentMsg));
return;
}
FT8::FT8::encode(a174, a77);
}
void ChirpChatModEncoderFT::encodeMsgBeaconOrCQ(const QString& myCall, const QString& myLocator, const QString& shorthand, int a174[])
{
int c28_1, c28_2, g15;
if (!FT8::Packing::packcall_std(c28_1, shorthand.toStdString())) //
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(shorthand));
return;
}
if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall));
return;
}
if (!FT8::Packing::packgrid(g15, myLocator.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode locator (%s)", qPrintable(myLocator));
return;
}
int a77[77];
std::fill(a77, a77 + 77, 0);
FT8::Packing::pack1(a77, c28_1, c28_2, g15, 0);
FT8::FT8::encode(a174, a77);
}
void ChirpChatModEncoderFT::encodeMsgReply(const QString& myCall, const QString& urCall, const QString& myLocator, int a174[])
{
int c28_1, c28_2, g15;
if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) //
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall));
return;
}
if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall));
return;
}
if (!FT8::Packing::packgrid(g15, myLocator.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode locator (%s)", qPrintable(myLocator));
return;
}
int a77[77];
std::fill(a77, a77 + 77, 0);
FT8::Packing::pack1(a77, c28_1, c28_2, g15, 0);
FT8::FT8::encode(a174, a77);
}
void ChirpChatModEncoderFT::encodeMsgReport(const QString& myCall, const QString& urCall, const QString& myReport, int reply, int a174[])
{
int c28_1, c28_2, g15;
if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) //
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall));
return;
}
if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall));
return;
}
if (!FT8::Packing::packgrid(g15, myReport.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode report (%s)", qPrintable(myReport));
return;
}
int a77[77];
std::fill(a77, a77 + 77, 0);
FT8::Packing::pack1(a77, c28_1, c28_2, g15, reply);
FT8::FT8::encode(a174, a77);
}
void ChirpChatModEncoderFT::encodeMsgFinish(const QString& myCall, const QString& urCall, const QString& shorthand, int a174[])
{
int c28_1, c28_2, g15;
if (!FT8::Packing::packcall_std(c28_1, urCall.toStdString())) //
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call1 (%s)", qPrintable(urCall));
return;
}
if (!FT8::Packing::packcall_std(c28_2, myCall.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode call2 (%s)", qPrintable(myCall));
return;
}
if (!FT8::Packing::packgrid(g15, shorthand.toStdString()))
{
qDebug("ChirpChatModEncoderFT::encodeMsgBeacon: failed to encode shorthand (%s)", qPrintable(shorthand));
return;
}
int a77[77];
std::fill(a77, a77 + 77, 0);
FT8::Packing::pack1(a77, c28_1, c28_2, g15, 0);
FT8::FT8::encode(a174, a77);
}
#endif // HAS_FT8

View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 as 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 V3 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 PLUGINS_CHANNELTX_MODCHIRPCHAT_CHIRPCHATMODENCODEFT_H_
#define PLUGINS_CHANNELTX_MODCHIRPCHAT_CHIRPCHATMODENCODEFT_H_
#include <vector>
#include <QString>
#include "chirpchatmodsettings.h"
class ChirpChatModEncoderFT
{
public:
static void encodeMsg(
const QString& myCall,
const QString& urCall,
const QString& myLocator,
const QString& myReport,
const QString& textMessage,
ChirpChatModSettings::MessageType messageType,
unsigned int nbSymbolBits,
std::vector<unsigned short>& symbols
);
private:
static void encodeTextMsg(const QString& text, int a174[]);
static void encodeMsgBeaconOrCQ(const QString& myCall, const QString& myLocator, const QString& shorthand, int a174[]);
static void encodeMsgReply(const QString& myCall, const QString& urCall, const QString& myLocator, int a174[]);
static void encodeMsgReport(const QString& myCall, const QString& urCall, const QString& myReport, int reply, int a174[]);
static void encodeMsgFinish(const QString& myCall, const QString& urCall, const QString& shorthand, int a174[]);
};
#endif // PLUGINS_CHANNELTX_MODCHIRPCHAT_CHIRPCHATMODENCODEFT_H_

View File

@ -551,6 +551,11 @@
<string>TTY</string> <string>TTY</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>FT</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>

View File

@ -34,7 +34,8 @@ struct ChirpChatModSettings
{ {
CodingLoRa, //!< Standard LoRa CodingLoRa, //!< Standard LoRa
CodingASCII, //!< plain ASCII (7 bits) CodingASCII, //!< plain ASCII (7 bits)
CodingTTY //!< plain TTY (5 bits) CodingTTY, //!< plain TTY (5 bits)
CodingFT //!< FT8/4 scheme (payload 174 bits LDPC)
}; };
enum MessageType enum MessageType

View File

@ -13,9 +13,7 @@ LoRa is a property of Semtech and the details of the protocol are not made publi
This LoRa encoder is designed for experimentation. For production grade applications it is recommended to use dedicated hardware instead. This LoRa encoder is designed for experimentation. For production grade applications it is recommended to use dedicated hardware instead.
Modulation characteristics from LoRa have been augmented with more bandwidths and FFT bin collations (DE factor). Plain TTY and ASCII have also been added and there are plans to add some more complex typically amateur radio MFSK based modes like JT65. Modulation characteristics from LoRa have been augmented with more bandwidths and FFT bin collations (DE factor). Plain TTY and ASCII have also been added that match character value to symbols directly. The FT protocol used in FT8 and FT4 is introduced packing the 174 bits payload into (SF -DE) bits symbols. There are plans to add some more of these typically amateur radio MFSK based modes like JT65.
Note: this plugin is officially supported since version 6.
<h2>Interface</h2> <h2>Interface</h2>
@ -106,6 +104,7 @@ To populate messages you can specify your callsign (10.5), the other party calls
- **LoRa**: LoRa compatible - **LoRa**: LoRa compatible
- **ASCII**: 7 bit plain ASCII without FEC and CRC. Requires exactly 7 bit effective samples thus SF-DE = 7 where SF is the spreading factor (5) and DE the distance enhancement factor (6) - **ASCII**: 7 bit plain ASCII without FEC and CRC. Requires exactly 7 bit effective samples thus SF-DE = 7 where SF is the spreading factor (5) and DE the distance enhancement factor (6)
- **TTY**: 5 bit Baudot (Teletype) without FEC and CRC. Requires exactly 5 bit effective samples thus SF-DE = 5 where SF is the spreading factor (5) and DE the distance enhancement factor (6) - **TTY**: 5 bit Baudot (Teletype) without FEC and CRC. Requires exactly 5 bit effective samples thus SF-DE = 5 where SF is the spreading factor (5) and DE the distance enhancement factor (6)
- **FT**: FT8/FT4 coding is applied using data in (10.5) to (10.8) to encode the 174 bit message payload with CRC and FEC as per FT8/FT4 protocol using a type 1 (standard) type of message. Note that the report (10.8) must comply with the FT rule (coded "-35" to "+99" with a leading 0 for the number) and would usually represent the integer part of the S/N ratio in the ChirpChat demodulator receiver. Calls should not be prefixed nor suffixed and the first 4 characters of the locator must represent a valid 4 character grid square. Plain text messages (13 characters) are also supported with the 0.0 type of message using the text entered in the message box (11). These 174 bits are packed into (SF - DE) bits symbols padded with zero bits if necessary. For the details of the FT protocol see: https://wsjt.sourceforge.io/FT4_FT8_QEX.pdf
<h4>10.2: Number of FEC parity bits (LoRa)</h4> <h4>10.2: Number of FEC parity bits (LoRa)</h4>
@ -157,6 +156,19 @@ This lets you choose which pre-formatted message to send:
- **Text**: plain text - **Text**: plain text
- **Bytes**: binary message in the form of a string of bytes. Use the hex window (12) to specify the message - **Bytes**: binary message in the form of a string of bytes. Use the hex window (12) to specify the message
In FT mode standard FT type messages are generated regardless of placeholders based on MyCall, YourCall, MyLoc, Report and Msg data (entered while in "Text" format). Locators are 4 character grids i.e. only the 4 first characters are taken. Reports must be valid FT reports from -35 to 99 coded as < sign>< zero padded value> (e.g -12, -04, +00, +04, +12) :
- **Beacon**: DE < MyCall > < MyLoc >
- **CQ**: CQ < MyCall> < MyLoc >
- **Reply**: < YourCall > < MyCall > < MyLoc >
- **Report**: < YourCall > < MyCall > < Report >
- **R-Report**: < YourCall > < MyCall > R< Report >
- **RRR**: < YourCall > < MyCall > RRR
- **73**: < YourCall > < MyCall > 73
- **QSO text**: < Msg >
- **Text**: < Msg >
- **Bytes**: < Msg >
<h4>10.10: Revert to standard messages</h4> <h4>10.10: Revert to standard messages</h4>
Reformat all predefined messages in standard messages with placeholders. The Generate button (13) replaces the placeholders with the given QSO elements (10.5 to 10.8) Reformat all predefined messages in standard messages with placeholders. The Generate button (13) replaces the placeholders with the given QSO elements (10.5 to 10.8)

View File

@ -574,6 +574,7 @@ void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QSt
uint16_t crcValue; uint16_t crcValue;
int len; int len;
int packet_length; int packet_length;
QStringList viaList = via.split(',', Qt::SkipEmptyParts);
// Create AX.25 packet // Create AX.25 packet
p = packet; p = packet;
@ -584,9 +585,10 @@ void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QSt
// Dest // Dest
p = ax25_address(p, to, 0xe0); p = ax25_address(p, to, 0xe0);
// From // From
p = ax25_address(p, callsign, 0x60); p = ax25_address(p, callsign, 0x60 | (viaList.empty() ? 0x01 : 0x00));
// Via // Via
p = ax25_address(p, via, 0x61); for (int i = 0; i < viaList.size(); i++)
p = ax25_address(p, std::move(viaList[i]), 0x60 | (i == viaList.size()-1 ? 0x01 : 0x00));
// Control // Control
*p++ = m_settings.m_ax25Control; *p++ = m_settings.m_ax25Control;
// PID // PID

View File

@ -186,8 +186,7 @@ void AudioInputGui::updateSampleRateAndFrequency()
void AudioInputGui::refreshDeviceList() void AudioInputGui::refreshDeviceList()
{ {
ui->device->blockSignals(true); ui->device->blockSignals(true);
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); const QList<AudioDeviceInfo>& audioList = AudioDeviceInfo::availableInputDevices();
const QList<AudioDeviceInfo>& audioList = audioDeviceManager->getInputDevices();
ui->device->clear(); ui->device->clear();
for (const auto &itAudio : audioList) for (const auto &itAudio : audioList)

View File

@ -174,7 +174,7 @@ void FCDProInput::closeDevice()
bool FCDProInput::openFCDAudio(const char* cardname) bool FCDProInput::openFCDAudio(const char* cardname)
{ {
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
const QList<AudioDeviceInfo>& audioList = audioDeviceManager->getInputDevices(); const QList<AudioDeviceInfo>& audioList = AudioDeviceInfo::availableInputDevices();
for (const auto &itAudio : audioList) for (const auto &itAudio : audioList)
{ {

View File

@ -176,7 +176,7 @@ void FCDProPlusInput::closeDevice()
bool FCDProPlusInput::openFCDAudio(const char* cardname) bool FCDProPlusInput::openFCDAudio(const char* cardname)
{ {
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
const QList<AudioDeviceInfo>& audioList = audioDeviceManager->getInputDevices(); const QList<AudioDeviceInfo>& audioList = AudioDeviceInfo::availableInputDevices();
for (const auto &itAudio : audioList) for (const auto &itAudio : audioList)
{ {

View File

@ -20,6 +20,10 @@
#include "audiodeviceinfo.h" #include "audiodeviceinfo.h"
bool inputDevicesEnumerated = false, outputDevicesEnumerated = false;
QList<AudioDeviceInfo> inputDevices, outputDevices;
AudioDeviceInfo defaultInputDevice_, defaultOutputDevice_;
QString AudioDeviceInfo::deviceName() const QString AudioDeviceInfo::deviceName() const
{ {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
@ -67,69 +71,81 @@ QString AudioDeviceInfo::realm() const
} }
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QList<AudioDeviceInfo> AudioDeviceInfo::availableInputDevices() const QList<AudioDeviceInfo> &AudioDeviceInfo::availableInputDevices()
{ {
QList<QAudioDevice> devInfos = QMediaDevices::audioInputs(); if (!inputDevicesEnumerated) {
QList<AudioDeviceInfo> list; QList<QAudioDevice> devInfos = QMediaDevices::audioInputs();
for (auto devInfo : devInfos) {
for (auto devInfo : devInfos) { inputDevices.append(AudioDeviceInfo(devInfo));
list.append(AudioDeviceInfo(devInfo)); }
inputDevicesEnumerated = true;
} }
return list; return inputDevices;
} }
QList<AudioDeviceInfo> AudioDeviceInfo::availableOutputDevices() const QList<AudioDeviceInfo> &AudioDeviceInfo::availableOutputDevices()
{ {
QList<QAudioDevice> devInfos = QMediaDevices::audioOutputs(); if (!outputDevicesEnumerated) {
QList<AudioDeviceInfo> list; QList<QAudioDevice> devInfos = QMediaDevices::audioOutputs();
for (auto devInfo : devInfos) {
for (auto devInfo : devInfos) { outputDevices.append(AudioDeviceInfo(devInfo));
list.append(AudioDeviceInfo(devInfo)); }
outputDevicesEnumerated = true;
} }
return list; return outputDevices;
} }
#else #else
QList<AudioDeviceInfo> AudioDeviceInfo::availableInputDevices() const QList<AudioDeviceInfo> &AudioDeviceInfo::availableInputDevices()
{ {
QList<QAudioDeviceInfo> devInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); if (!inputDevicesEnumerated) {
QList<AudioDeviceInfo> list; QList<QAudioDeviceInfo> devInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
for (auto devInfo : devInfos) {
for (auto devInfo : devInfos) { inputDevices.append(AudioDeviceInfo(devInfo));
list.append(AudioDeviceInfo(devInfo)); }
inputDevicesEnumerated = true;
} }
return list; return inputDevices;
} }
QList<AudioDeviceInfo> AudioDeviceInfo::availableOutputDevices() const QList<AudioDeviceInfo> &AudioDeviceInfo::availableOutputDevices()
{ {
QList<QAudioDeviceInfo> devInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); if (!outputDevicesEnumerated) {
QList<AudioDeviceInfo> list; QList<QAudioDeviceInfo> devInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
for (auto devInfo : devInfos) {
for (auto devInfo : devInfos) { outputDevices.append(AudioDeviceInfo(devInfo));
list.append(AudioDeviceInfo(devInfo)); }
outputDevicesEnumerated = true;
} }
return list; return outputDevices;
} }
#endif #endif
AudioDeviceInfo AudioDeviceInfo::defaultOutputDevice() const AudioDeviceInfo &AudioDeviceInfo::defaultOutputDevice()
{ {
if (defaultOutputDevice_.m_deviceInfo.isNull())
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return AudioDeviceInfo(QMediaDevices::defaultAudioOutput()); defaultOutputDevice_ = AudioDeviceInfo(QMediaDevices::defaultAudioOutput());
#else #else
return AudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice()); defaultOutputDevice_ = AudioDeviceInfo(QAudioDeviceInfo::defaultOutputDevice());
#endif #endif
}
return defaultOutputDevice_;
} }
AudioDeviceInfo AudioDeviceInfo::defaultInputDevice() const AudioDeviceInfo &AudioDeviceInfo::defaultInputDevice()
{ {
if (defaultInputDevice_.m_deviceInfo.isNull())
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
return AudioDeviceInfo(QMediaDevices::defaultAudioInput()); defaultInputDevice_ = AudioDeviceInfo(QMediaDevices::defaultAudioInput());
#else #else
return AudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice()); defaultInputDevice_ = AudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice());
#endif #endif
}
return defaultInputDevice_;
} }

View File

@ -67,10 +67,10 @@ public:
bool isFormatSupported(const QAudioFormat &settings) const; bool isFormatSupported(const QAudioFormat &settings) const;
QList<int> supportedSampleRates() const; QList<int> supportedSampleRates() const;
static QList<AudioDeviceInfo> availableInputDevices(); static const QList<AudioDeviceInfo> &availableInputDevices();
static QList<AudioDeviceInfo> availableOutputDevices(); static const QList<AudioDeviceInfo> &availableOutputDevices();
static AudioDeviceInfo defaultInputDevice(); static const AudioDeviceInfo &defaultInputDevice();
static AudioDeviceInfo defaultOutputDevice(); static const AudioDeviceInfo &defaultOutputDevice();
private: private:

View File

@ -82,20 +82,22 @@ QDataStream& operator>>(QDataStream& ds, AudioDeviceManager::OutputDeviceInfo& i
AudioDeviceManager::AudioDeviceManager() AudioDeviceManager::AudioDeviceManager()
{ {
qDebug("AudioDeviceManager::AudioDeviceManager: scan input devices"); qDebug("AudioDeviceManager::AudioDeviceManager: scan input devices");
m_inputDevicesInfo = AudioDeviceInfo::availableInputDevices(); {
auto &devicesInfo = AudioDeviceInfo::availableInputDevices();
for (int i = 0; i < m_inputDevicesInfo.size(); i++) { for (int i = 0; i < devicesInfo.size(); i++) {
qDebug("AudioDeviceManager::AudioDeviceManager: input device #%d: %s", i, qPrintable(m_inputDevicesInfo[i].deviceName())); qDebug("AudioDeviceManager::AudioDeviceManager: input device #%d: %s", i, qPrintable(devicesInfo[i].deviceName()));
}
} }
qDebug("AudioDeviceManager::AudioDeviceManager: scan output devices"); qDebug("AudioDeviceManager::AudioDeviceManager: scan output devices");
m_outputDevicesInfo = AudioDeviceInfo::availableOutputDevices(); {
auto &devicesInfo = AudioDeviceInfo::availableOutputDevices();
for (int i = 0; i < m_outputDevicesInfo.size(); i++) { for (int i = 0; i < devicesInfo.size(); i++) {
qDebug("AudioDeviceManager::AudioDeviceManager: output device #%d: %s", i, qPrintable(m_outputDevicesInfo[i].deviceName())); qDebug("AudioDeviceManager::AudioDeviceManager: output device #%d: %s", i, qPrintable(devicesInfo[i].deviceName()));
}
} }
m_defaultInputStarted = false; m_defaultInputStarted = false;
m_defaultOutputStarted = false; m_defaultOutputStarted = false;
@ -142,9 +144,9 @@ bool AudioDeviceManager::getOutputDeviceName(int outputDeviceIndex, QString &dev
} }
else else
{ {
if (outputDeviceIndex < m_outputDevicesInfo.size()) if (outputDeviceIndex < AudioDeviceInfo::availableOutputDevices().size())
{ {
deviceName = m_outputDevicesInfo[outputDeviceIndex].deviceName(); deviceName = AudioDeviceInfo::availableOutputDevices()[outputDeviceIndex].deviceName();
return true; return true;
} }
else else
@ -163,9 +165,9 @@ bool AudioDeviceManager::getInputDeviceName(int inputDeviceIndex, QString &devic
} }
else else
{ {
if (inputDeviceIndex < m_inputDevicesInfo.size()) if (inputDeviceIndex < AudioDeviceInfo::availableInputDevices().size())
{ {
deviceName = m_inputDevicesInfo[inputDeviceIndex].deviceName(); deviceName = AudioDeviceInfo::availableInputDevices()[inputDeviceIndex].deviceName();
return true; return true;
} }
else else
@ -177,10 +179,10 @@ bool AudioDeviceManager::getInputDeviceName(int inputDeviceIndex, QString &devic
int AudioDeviceManager::getOutputDeviceIndex(const QString &deviceName) const int AudioDeviceManager::getOutputDeviceIndex(const QString &deviceName) const
{ {
for (int i = 0; i < m_outputDevicesInfo.size(); i++) for (int i = 0; i < AudioDeviceInfo::availableOutputDevices().size(); i++)
{ {
//qDebug("AudioDeviceManager::getOutputDeviceIndex: %d: %s|%s", i, qPrintable(deviceName), qPrintable(m_outputDevicesInfo[i].deviceName())); //qDebug("AudioDeviceManager::getOutputDeviceIndex: %d: %s|%s", i, qPrintable(deviceName), qPrintable(AudioDeviceInfo::availableOutputDevices()[i].deviceName()));
if (deviceName == m_outputDevicesInfo[i].deviceName()) { if (deviceName == AudioDeviceInfo::availableOutputDevices()[i].deviceName()) {
return i; return i;
} }
} }
@ -190,10 +192,10 @@ int AudioDeviceManager::getOutputDeviceIndex(const QString &deviceName) const
int AudioDeviceManager::getInputDeviceIndex(const QString &deviceName) const int AudioDeviceManager::getInputDeviceIndex(const QString &deviceName) const
{ {
for (int i = 0; i < m_inputDevicesInfo.size(); i++) for (int i = 0; i < AudioDeviceInfo::availableInputDevices().size(); i++)
{ {
//qDebug("AudioDeviceManager::getInputDeviceIndex: %d: %s|%s", i, qPrintable(deviceName), qPrintable(m_inputDevicesInfo[i].deviceName())); //qDebug("AudioDeviceManager::getInputDeviceIndex: %d: %s|%s", i, qPrintable(deviceName), qPrintable(AudioDeviceInfo::availableInputDevices()[i].deviceName()));
if (deviceName == m_inputDevicesInfo[i].deviceName()) { if (deviceName == AudioDeviceInfo::availableInputDevices()[i].deviceName()) {
return i; return i;
} }
} }
@ -295,7 +297,7 @@ void AudioDeviceManager::addAudioSink(AudioFifo* audioFifo, MessageQueue *sample
if (outputDeviceIndex < 0) { if (outputDeviceIndex < 0) {
audioOutputDevice->setDeviceName("System default"); audioOutputDevice->setDeviceName("System default");
} else { } else {
audioOutputDevice->setDeviceName(m_outputDevicesInfo[outputDeviceIndex].deviceName()); audioOutputDevice->setDeviceName(AudioDeviceInfo::availableOutputDevices()[outputDeviceIndex].deviceName());
} }
qDebug("AudioDeviceManager::addAudioSink: new AudioOutputDevice on thread: %p", thread); qDebug("AudioDeviceManager::addAudioSink: new AudioOutputDevice on thread: %p", thread);
@ -382,7 +384,7 @@ void AudioDeviceManager::addAudioSource(AudioFifo* audioFifo, MessageQueue *samp
if (inputDeviceIndex < 0) { if (inputDeviceIndex < 0) {
audioInputDevice->setDeviceName("System default"); audioInputDevice->setDeviceName("System default");
} else { } else {
audioInputDevice->setDeviceName(m_outputDevicesInfo[inputDeviceIndex].deviceName()); audioInputDevice->setDeviceName(AudioDeviceInfo::availableOutputDevices()[inputDeviceIndex].deviceName());
} }
qDebug("AudioDeviceManager::addAudioSource: new AudioInputDevice on thread: %p", thread); qDebug("AudioDeviceManager::addAudioSource: new AudioInputDevice on thread: %p", thread);
@ -500,7 +502,7 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
m_audioOutputInfos[deviceName].udpChannelMode = udpChannelMode; m_audioOutputInfos[deviceName].udpChannelMode = udpChannelMode;
m_audioOutputInfos[deviceName].udpChannelCodec = udpChannelCodec; m_audioOutputInfos[deviceName].udpChannelCodec = udpChannelCodec;
m_audioOutputInfos[deviceName].udpDecimationFactor = decimationFactor; m_audioOutputInfos[deviceName].udpDecimationFactor = decimationFactor;
m_defaultOutputStarted = (outputDeviceIndex == -1); m_defaultOutputStarted |= (outputDeviceIndex == -1);
} }
else else
{ {
@ -538,7 +540,7 @@ void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
m_audioInputs[inputDeviceIndex]->setVolume(volume); m_audioInputs[inputDeviceIndex]->setVolume(volume);
m_audioInputInfos[deviceName].volume = volume; m_audioInputInfos[deviceName].volume = volume;
m_defaultInputStarted = (inputDeviceIndex == -1); m_defaultInputStarted |= (inputDeviceIndex == -1);
} }
else else
{ {
@ -789,9 +791,9 @@ void AudioDeviceManager::inputInfosCleanup()
{ {
QSet<QString> deviceNames; QSet<QString> deviceNames;
deviceNames.insert(m_defaultDeviceName); deviceNames.insert(m_defaultDeviceName);
QList<AudioDeviceInfo>::const_iterator itd = m_inputDevicesInfo.begin(); QList<AudioDeviceInfo>::const_iterator itd = AudioDeviceInfo::availableInputDevices().begin();
for (; itd != m_inputDevicesInfo.end(); ++itd) for (; itd != AudioDeviceInfo::availableInputDevices().end(); ++itd)
{ {
qDebug("AudioDeviceManager::inputInfosCleanup: device: %s", qPrintable(itd->deviceName())); qDebug("AudioDeviceManager::inputInfosCleanup: device: %s", qPrintable(itd->deviceName()));
deviceNames.insert(itd->deviceName()); deviceNames.insert(itd->deviceName());
@ -817,9 +819,9 @@ void AudioDeviceManager::outputInfosCleanup()
{ {
QSet<QString> deviceNames; QSet<QString> deviceNames;
deviceNames.insert(m_defaultDeviceName); deviceNames.insert(m_defaultDeviceName);
QList<AudioDeviceInfo>::const_iterator itd = m_outputDevicesInfo.begin(); QList<AudioDeviceInfo>::const_iterator itd = AudioDeviceInfo::availableOutputDevices().begin();
for (; itd != m_outputDevicesInfo.end(); ++itd) for (; itd != AudioDeviceInfo::availableOutputDevices().end(); ++itd)
{ {
qDebug("AudioDeviceManager::outputInfosCleanup: device: %s", qPrintable(itd->deviceName())); qDebug("AudioDeviceManager::outputInfosCleanup: device: %s", qPrintable(itd->deviceName()));
deviceNames.insert(itd->deviceName()); deviceNames.insert(itd->deviceName());

View File

@ -102,9 +102,6 @@ public:
AudioDeviceManager(); AudioDeviceManager();
~AudioDeviceManager(); ~AudioDeviceManager();
const QList<AudioDeviceInfo>& getInputDevices() const { return m_inputDevicesInfo; }
const QList<AudioDeviceInfo>& getOutputDevices() const { return m_outputDevicesInfo; }
bool getOutputDeviceName(int outputDeviceIndex, QString &deviceName) const; bool getOutputDeviceName(int outputDeviceIndex, QString &deviceName) const;
bool getInputDeviceName(int inputDeviceIndex, QString &deviceName) const; bool getInputDeviceName(int inputDeviceIndex, QString &deviceName) const;
int getOutputDeviceIndex(const QString &deviceName) const; int getOutputDeviceIndex(const QString &deviceName) const;
@ -136,9 +133,6 @@ public:
static const QString m_defaultDeviceName; static const QString m_defaultDeviceName;
private: private:
QList<AudioDeviceInfo> m_inputDevicesInfo;
QList<AudioDeviceInfo> m_outputDevicesInfo;
QMap<AudioFifo*, int> m_audioSinkFifos; //< audio sink FIFO to audio output device index-1 map QMap<AudioFifo*, int> m_audioSinkFifos; //< audio sink FIFO to audio output device index-1 map
QMap<AudioFifo*, MessageQueue*> m_audioFifoToSinkMessageQueues; //!< audio sink FIFO to attached sink message queue QMap<AudioFifo*, MessageQueue*> m_audioFifoToSinkMessageQueues; //!< audio sink FIFO to attached sink message queue
QMap<int, QList<MessageQueue*> > m_outputDeviceSinkMessageQueues; //!< sink message queues attached to device QMap<int, QList<MessageQueue*> > m_outputDeviceSinkMessageQueues; //!< sink message queues attached to device

View File

@ -91,7 +91,7 @@ bool AudioOutputDevice::start(int deviceIndex, int sampleRate)
} }
else else
{ {
QList<AudioDeviceInfo> devicesInfo = AudioDeviceInfo::availableOutputDevices(); auto &devicesInfo = AudioDeviceInfo::availableOutputDevices();
if (deviceIndex < devicesInfo.size()) if (deviceIndex < devicesInfo.size())
{ {

View File

@ -61,6 +61,7 @@ bool AX25Packet::decode(QByteArray packet)
// List of repeater addresses for via field // List of repeater addresses for via field
m_via = QString(""); m_via = QString("");
i = 13; i = 13;
int incomingViaStrIdx = -1;
while ((packet[i] & 1) == 0) while ((packet[i] & 1) == 0)
{ {
i++; i++;
@ -72,11 +73,17 @@ bool AX25Packet::decode(QByteArray packet)
ssid = (repeaterSSID >> 1) & 0xf; ssid = (repeaterSSID >> 1) & 0xf;
QString repeater = QString(repeaterAddress).trimmed(); QString repeater = QString(repeaterAddress).trimmed();
QString ssidString = (ssid != 0) ? QString("%2-%3").arg(repeater).arg(ssid) : QString(repeater); QString ssidString = (ssid != 0) ? QString("%2-%3").arg(repeater).arg(ssid) : QString(repeater);
if (m_via == "")
m_via = ssidString; if (!m_via.isEmpty())
else m_via.append(',');
m_via = QString("%1,%2").arg(m_via).arg(ssidString); m_via.append(ssidString);
if (packet[i] & 0x80)
incomingViaStrIdx = m_via.length();
} }
if (incomingViaStrIdx >= 0)
m_via.insert(incomingViaStrIdx, "*");
i++; i++;
// Control can be 1 or 2 bytes - how to know if 2? // Control can be 1 or 2 bytes - how to know if 2?
// I, U and S frames // I, U and S frames

View File

@ -454,8 +454,8 @@ int WebAPIAdapter::instanceAudioGet(
{ {
(void) error; (void) error;
DSPEngine *dspEngine = DSPEngine::instance(); DSPEngine *dspEngine = DSPEngine::instance();
const QList<AudioDeviceInfo>& audioInputDevices = dspEngine->getAudioDeviceManager()->getInputDevices(); const QList<AudioDeviceInfo>& audioInputDevices = AudioDeviceInfo::availableInputDevices();
const QList<AudioDeviceInfo>& audioOutputDevices = dspEngine->getAudioDeviceManager()->getOutputDevices(); const QList<AudioDeviceInfo>& audioOutputDevices = AudioDeviceInfo::availableOutputDevices();
int nbInputDevices = audioInputDevices.size(); int nbInputDevices = audioInputDevices.size();
int nbOutputDevices = audioOutputDevices.size(); int nbOutputDevices = audioOutputDevices.size();

View File

@ -34,7 +34,6 @@ void MainBench::testFT8(const QString& wavFile, const QString& argsStr)
#include "ft8/ft8.h" #include "ft8/ft8.h"
#include "ft8/packing.h" #include "ft8/packing.h"
#include "ft8/pack0.h"
class TestFT8Protocols class TestFT8Protocols
{ {
@ -99,16 +98,16 @@ void TestFT8Protocols::testMsg1(const QStringList& argElements, bool runLDPC)
std::string locstr; std::string locstr;
int report; int reply;
if (argElements[3].startsWith("R+") || argElements[3].startsWith("R-")) if (argElements[3].startsWith("R+") || argElements[3].startsWith("R-"))
{ {
report = 1; reply = 1;
locstr = argElements[3].mid(1).toStdString(); locstr = argElements[3].mid(1).toStdString();
} }
else else
{ {
report = 0; reply = 0;
locstr = argElements[3].toStdString(); locstr = argElements[3].toStdString();
} }
@ -122,20 +121,13 @@ void TestFT8Protocols::testMsg1(const QStringList& argElements, bool runLDPC)
int a77[77]; int a77[77];
std::fill(a77, a77 + 77, 0); std::fill(a77, a77 + 77, 0);
FT8::Packing::pack1(a77, c28_1, c28_2, g15, reply);
FT8::pa64(a77, 0, 28, c28_1);
FT8::pa64(a77, 28+1, 28, c28_2);
a77[28+1+28+1] = report;
FT8::pa64(a77, 28+1+28+2, 15, g15);
FT8::pa64(a77, 28+1+28+2+15, 3, 1);
FT8::Packing packing; FT8::Packing packing;
std::string call1, call2, loc; std::string call1, call2, loc;
std::string msg = packing.unpack_1(a77, call1, call2, loc); std::string msg = packing.unpack_1(a77, call1, call2, loc);
qInfo("TestFT8Protocols::testMsg1: msg: %s, call1: %s, call2: %s, loc: %s", msg.c_str(), call1.c_str(), call2.c_str(), loc.c_str()); qInfo("TestFT8Protocols::testMsg1: msg: %s, call1: %s, call2: %s, loc: %s", msg.c_str(), call1.c_str(), call2.c_str(), loc.c_str());
if (runLDPC) if (runLDPC)
{ {
if (testLDPC(a77)) { if (testLDPC(a77)) {

View File

@ -40,14 +40,14 @@ AudioDialogX::AudioDialogX(AudioDeviceManager* audioDeviceManager, QWidget* pare
// out panel // out panel
AudioDeviceManager::OutputDeviceInfo outDeviceInfo; AudioDeviceManager::OutputDeviceInfo outDeviceInfo;
AudioDeviceInfo defaultOutputDeviceInfo = AudioDeviceInfo::defaultOutputDevice(); const AudioDeviceInfo &defaultOutputDeviceInfo = AudioDeviceInfo::defaultOutputDevice();
treeItem = new QTreeWidgetItem(ui->audioOutTree); treeItem = new QTreeWidgetItem(ui->audioOutTree);
treeItem->setText(1, AudioDeviceManager::m_defaultDeviceName); treeItem->setText(1, AudioDeviceManager::m_defaultDeviceName);
bool found = m_audioDeviceManager->getOutputDeviceInfo(AudioDeviceManager::m_defaultDeviceName, outDeviceInfo); bool found = m_audioDeviceManager->getOutputDeviceInfo(AudioDeviceManager::m_defaultDeviceName, outDeviceInfo);
treeItem->setText(0, found ? "__" : "_D"); treeItem->setText(0, found ? "__" : "_D");
ui->audioOutTree->setCurrentItem(treeItem); ui->audioOutTree->setCurrentItem(treeItem);
const QList<AudioDeviceInfo>& outputDevices = m_audioDeviceManager->getOutputDevices(); const QList<AudioDeviceInfo>& outputDevices = AudioDeviceInfo::availableOutputDevices();
for(QList<AudioDeviceInfo>::const_iterator it = outputDevices.begin(); it != outputDevices.end(); ++it) for(QList<AudioDeviceInfo>::const_iterator it = outputDevices.begin(); it != outputDevices.end(); ++it)
{ {
@ -75,7 +75,7 @@ AudioDialogX::AudioDialogX(AudioDeviceManager* audioDeviceManager, QWidget* pare
treeItem->setText(0, found ? "__" : "_D"); treeItem->setText(0, found ? "__" : "_D");
ui->audioInTree->setCurrentItem(treeItem); ui->audioInTree->setCurrentItem(treeItem);
const QList<AudioDeviceInfo>& inputDevices = m_audioDeviceManager->getInputDevices(); const QList<AudioDeviceInfo>& inputDevices = AudioDeviceInfo::availableInputDevices();
for(QList<AudioDeviceInfo>::const_iterator it = inputDevices.begin(); it != inputDevices.end(); ++it) for(QList<AudioDeviceInfo>::const_iterator it = inputDevices.begin(); it != inputDevices.end(); ++it)
{ {

View File

@ -40,7 +40,7 @@ AudioSelectDialog::AudioSelectDialog(const AudioDeviceManager* audioDeviceManage
defaultItem->setText(2, tr("%1").arg(sampleRate)); defaultItem->setText(2, tr("%1").arg(sampleRate));
defaultItem->setTextAlignment(2, Qt::AlignRight); defaultItem->setTextAlignment(2, Qt::AlignRight);
QList<AudioDeviceInfo> devices = input ? m_audioDeviceManager->getInputDevices() : m_audioDeviceManager->getOutputDevices(); const QList<AudioDeviceInfo> &devices = input ? AudioDeviceInfo::availableInputDevices() : AudioDeviceInfo::availableOutputDevices();
for(QList<AudioDeviceInfo>::const_iterator it = devices.begin(); it != devices.end(); ++it) for(QList<AudioDeviceInfo>::const_iterator it = devices.begin(); it != devices.end(); ++it)
{ {