mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-21 23:55:13 -05:00
M17 modulator: SMS packet +
This commit is contained in:
parent
dd2233f763
commit
f10da64717
@ -39,6 +39,7 @@ public:
|
||||
using codec_frame_t = std::array<uint8_t, 16>;
|
||||
using payload_t = std::array<uint8_t, 34>; // Bytes in the payload of a data frame.
|
||||
using frame_t = std::array<uint8_t, 46>; // M17 frame (without sync word).
|
||||
using packet_t = std::array<uint8_t, 25>; // Packet payload
|
||||
|
||||
static constexpr std::array<uint8_t, 2> SYNC_WORD = {0x32, 0x43};
|
||||
static constexpr std::array<uint8_t, 2> LSF_SYNC_WORD = {0x55, 0xF7};
|
||||
@ -57,6 +58,31 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
static std::array<int8_t, N * 4> bytes_to_symbols(const std::array<T, N>& bytes)
|
||||
{
|
||||
std::array<int8_t, N * 4> result;
|
||||
size_t index = 0;
|
||||
|
||||
for (auto b : bytes)
|
||||
{
|
||||
for (size_t i = 0; i != 4; ++i)
|
||||
{
|
||||
result[index++] = bits_to_symbol(b >> 6);
|
||||
b <<= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
void make_preamble(std::array<uint8_t, 48>& preamble_bytes)
|
||||
{
|
||||
// Preamble is simple... bytes -> symbols.
|
||||
preamble_bytes.fill(0x77);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode each LSF segment into a Golay-encoded LICH segment bitstream.
|
||||
*/
|
||||
@ -109,7 +135,7 @@ public:
|
||||
/**
|
||||
* Construct the link setup frame and split into LICH segments.
|
||||
*/
|
||||
void make_link_setup(lich_t& lich, mobilinkd::M17Modulator::frame_t lsf)
|
||||
void make_link_setup(lich_t& lich, mobilinkd::M17Modulator::frame_t lsf_frame)
|
||||
{
|
||||
using namespace mobilinkd;
|
||||
|
||||
@ -141,12 +167,14 @@ public:
|
||||
}
|
||||
|
||||
auto encoded = conv_encode(lsf);
|
||||
auto size = puncture_bytes(encoded, lsf_frame, P1);
|
||||
|
||||
auto size = puncture_bytes(encoded, lsf, P1);
|
||||
assert(size == 368);
|
||||
if (size != 368) {
|
||||
std::cerr << "make_link_setup: incorrect size (not 368)" << size;
|
||||
}
|
||||
|
||||
interleaver_.interleave(lsf);
|
||||
randomizer_(lsf);
|
||||
interleaver_.interleave(lsf_frame);
|
||||
randomizer_(lsf_frame);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +196,7 @@ public:
|
||||
* Assemble the audio frame payload by appending the frame number, encoded audio,
|
||||
* and CRC, then convolutionally coding and puncturing the data.
|
||||
*/
|
||||
payload_t make_payload(uint16_t frame_number, const codec_frame_t& payload)
|
||||
payload_t make_audio_payload(uint16_t frame_number, const codec_frame_t& payload)
|
||||
{
|
||||
std::array<uint8_t, 20> data; // FN, Audio, CRC = 2 + 16 + 2;
|
||||
data[0] = uint8_t((frame_number >> 8) & 0xFF);
|
||||
@ -188,7 +216,47 @@ public:
|
||||
|
||||
payload_t punctured;
|
||||
auto size = puncture_bytes(encoded, punctured, mobilinkd::P2);
|
||||
assert(size == 272);
|
||||
|
||||
if (size != 272) {
|
||||
std::cerr << "mobilinkd::M17Modulator::make_audio_payload: incorrect size (not 272)" << size;
|
||||
}
|
||||
|
||||
return punctured;
|
||||
}
|
||||
|
||||
frame_t make_packet_frame(uint8_t packet_number, bool last_packet, packet_t packet, int packet_size)
|
||||
{
|
||||
std::array<uint8_t, 26> packet_assembly;
|
||||
packet_assembly.fill(0);
|
||||
std::copy(packet.begin(), packet.begin() + packet_size, packet_assembly.begin());
|
||||
|
||||
if (packet_number == 0) {
|
||||
crc_.reset();
|
||||
}
|
||||
|
||||
for (int i = 0; i < packet_size; i++) {
|
||||
crc_(packet[i]);
|
||||
}
|
||||
|
||||
if (last_packet)
|
||||
{
|
||||
packet_assembly[25] = 0x80 | (packet_size<<2);
|
||||
packet_assembly[packet_size] = crc_.get_bytes()[1];
|
||||
packet_assembly[packet_size+1] = crc_.get_bytes()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_assembly[25] = (packet_number<<2);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 2*26+1> encoded = conv_encode(packet_assembly);
|
||||
frame_t punctured;
|
||||
auto size = puncture_bytes(encoded, punctured, mobilinkd::P3);
|
||||
|
||||
if (size != 368) {
|
||||
std::cerr << "mobilinkd::M17Modulator::make_packet_frame: incorrect size (not 368)" << size;
|
||||
}
|
||||
|
||||
return punctured;
|
||||
}
|
||||
|
||||
@ -280,24 +348,6 @@ private:
|
||||
return encoded_call;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
static std::array<int8_t, N * 4> bytes_to_symbols(const std::array<T, N>& bytes)
|
||||
{
|
||||
std::array<int8_t, N * 4> result;
|
||||
size_t index = 0;
|
||||
|
||||
for (auto b : bytes)
|
||||
{
|
||||
for (size_t i = 0; i != 4; ++i)
|
||||
{
|
||||
result[index++] = bits_to_symbol(b >> 6);
|
||||
b <<= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
static std::array<T, N * 2 + 1> conv_encode(std::array<T, N> data)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ set(modm17_SOURCES
|
||||
m17mod.cpp
|
||||
m17modbaseband.cpp
|
||||
m17modsource.cpp
|
||||
m17modprocessor.cpp
|
||||
m17modplugin.cpp
|
||||
m17modsettings.cpp
|
||||
m17modwebapiadapter.cpp
|
||||
@ -13,6 +14,7 @@ set(modm17_HEADERS
|
||||
m17mod.h
|
||||
m17modbaseband.h
|
||||
m17modsource.h
|
||||
m17modprocessor.h
|
||||
m17modplugin.h
|
||||
m17modsettings.h
|
||||
m17modwebapiadapter.h
|
||||
@ -20,6 +22,8 @@ set(modm17_HEADERS
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${CODEC2_INCLUDE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/modems
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
@ -48,12 +52,18 @@ add_library(${TARGET_NAME} SHARED
|
||||
${modm17_SOURCES}
|
||||
)
|
||||
|
||||
if(CODEC2_EXTERNAL)
|
||||
add_dependencies(${TARGET_NAME} codec2)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
swagger
|
||||
${CODEC2_LIBRARIES}
|
||||
modems
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
@ -294,7 +294,9 @@ void M17Mod::applySettings(const M17ModSettings& settings, bool force)
|
||||
<< " m_toneFrequency: " << settings.m_toneFrequency
|
||||
<< " m_channelMute: " << settings.m_channelMute
|
||||
<< " m_playLoop: " << settings.m_playLoop
|
||||
<< " m_modAFInput " << settings.m_modAFInput
|
||||
<< " m_m17Mode " << settings.m_m17Mode
|
||||
<< " m_audioType " << settings.m_audioType
|
||||
<< " m_packetType " << settings.m_packetType
|
||||
<< " m_audioDeviceName: " << settings.m_audioDeviceName
|
||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
||||
@ -320,8 +322,14 @@ void M17Mod::applySettings(const M17ModSettings& settings, bool force)
|
||||
if ((settings.m_playLoop != m_settings.m_playLoop) || force) {
|
||||
reverseAPIKeys.append("playLoop");
|
||||
}
|
||||
if ((settings.m_modAFInput != m_settings.m_modAFInput) || force) {
|
||||
reverseAPIKeys.append("modAFInput");
|
||||
if ((settings.m_audioType != m_settings.m_audioType) || force) {
|
||||
reverseAPIKeys.append("audioType");
|
||||
}
|
||||
if ((settings.m_packetType != m_settings.m_packetType) || force) {
|
||||
reverseAPIKeys.append("packetType");
|
||||
}
|
||||
if ((settings.m_m17Mode != m_settings.m_m17Mode) || force) {
|
||||
reverseAPIKeys.append("m17Mode");
|
||||
}
|
||||
if((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) {
|
||||
reverseAPIKeys.append("rfBandwidth");
|
||||
@ -470,8 +478,14 @@ void M17Mod::webapiUpdateChannelSettings(
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
|
||||
settings.m_inputFrequencyOffset = response.getM17ModSettings()->getInputFrequencyOffset();
|
||||
}
|
||||
if (channelSettingsKeys.contains("modAFInput")) {
|
||||
settings.m_modAFInput = (M17ModSettings::M17ModInputAF) response.getM17ModSettings()->getModAfInput();
|
||||
if (channelSettingsKeys.contains("m17Mode")) {
|
||||
settings.m_m17Mode = (M17ModSettings::M17Mode) response.getM17ModSettings()->getM17Mode();
|
||||
}
|
||||
if (channelSettingsKeys.contains("audioType")) {
|
||||
settings.m_audioType = (M17ModSettings::AudioType) response.getM17ModSettings()->getAudioType();
|
||||
}
|
||||
if (channelSettingsKeys.contains("packetType")) {
|
||||
settings.m_packetType = (M17ModSettings::PacketType) response.getM17ModSettings()->getPacketType();
|
||||
}
|
||||
if (channelSettingsKeys.contains("playLoop")) {
|
||||
settings.m_playLoop = response.getM17ModSettings()->getPlayLoop() != 0;
|
||||
@ -533,7 +547,9 @@ void M17Mod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& respon
|
||||
response.getM17ModSettings()->setChannelMute(settings.m_channelMute ? 1 : 0);
|
||||
response.getM17ModSettings()->setFmDeviation(settings.m_fmDeviation);
|
||||
response.getM17ModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
response.getM17ModSettings()->setModAfInput((int) settings.m_modAFInput);
|
||||
response.getM17ModSettings()->setM17Mode((int) settings.m_m17Mode);
|
||||
response.getM17ModSettings()->setAudioType((int) settings.m_audioType);
|
||||
response.getM17ModSettings()->setPacketType((int) settings.m_packetType);
|
||||
response.getM17ModSettings()->setPlayLoop(settings.m_playLoop ? 1 : 0);
|
||||
response.getM17ModSettings()->setRfBandwidth(settings.m_rfBandwidth);
|
||||
response.getM17ModSettings()->setRgbColor(settings.m_rgbColor);
|
||||
@ -673,8 +689,14 @@ void M17Mod::webapiFormatChannelSettings(
|
||||
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
|
||||
swgM17ModSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
|
||||
}
|
||||
if (channelSettingsKeys.contains("modAFInput") || force) {
|
||||
swgM17ModSettings->setModAfInput((int) settings.m_modAFInput);
|
||||
if (channelSettingsKeys.contains("m17Mode") || force) {
|
||||
swgM17ModSettings->setM17Mode((int) settings.m_m17Mode);
|
||||
}
|
||||
if (channelSettingsKeys.contains("audioType") || force) {
|
||||
swgM17ModSettings->setAudioType((int) settings.m_audioType);
|
||||
}
|
||||
if (channelSettingsKeys.contains("packetType") || force) {
|
||||
swgM17ModSettings->setPacketType((int) settings.m_packetType);
|
||||
}
|
||||
if (channelSettingsKeys.contains("audioDeviceName") || force) {
|
||||
swgM17ModSettings->setAudioDeviceName(new QString(settings.m_audioDeviceName));
|
||||
@ -764,3 +786,8 @@ int M17Mod::getFeedbackAudioSampleRate() const
|
||||
{
|
||||
return m_basebandSource->getFeedbackAudioSampleRate();
|
||||
}
|
||||
|
||||
void M17Mod::sendPacket()
|
||||
{
|
||||
m_basebandSource->sendPacket();
|
||||
}
|
||||
|
@ -233,6 +233,7 @@ public:
|
||||
uint32_t getNumberOfDeviceStreams() const;
|
||||
int getAudioSampleRate() const;
|
||||
int getFeedbackAudioSampleRate() const;
|
||||
void sendPacket();
|
||||
|
||||
static const char* const m_channelIdURI;
|
||||
static const char* const m_channelId;
|
||||
|
@ -196,12 +196,12 @@ void M17ModBaseband::applySettings(const M17ModSettings& settings, bool force)
|
||||
}
|
||||
}
|
||||
|
||||
if ((settings.m_modAFInput != m_settings.m_modAFInput) || force)
|
||||
if ((settings.m_audioType != m_settings.m_audioType) || force)
|
||||
{
|
||||
AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager();
|
||||
int audioDeviceIndex = audioDeviceManager->getInputDeviceIndex(settings.m_audioDeviceName);
|
||||
|
||||
if (settings.m_modAFInput == M17ModSettings::M17ModInputAudio) {
|
||||
if (settings.m_audioType == M17ModSettings::AudioInput) {
|
||||
audioDeviceManager->addAudioSource(getAudioFifo(), getInputMessageQueue(), audioDeviceIndex);
|
||||
} else {
|
||||
audioDeviceManager->removeAudioSource(getAudioFifo());
|
||||
|
@ -70,6 +70,7 @@ public:
|
||||
AudioFifo *getAudioFifo() { return m_source.getAudioFifo(); }
|
||||
AudioFifo *getFeedbackAudioFifo() { return m_source.getFeedbackAudioFifo(); }
|
||||
void setChannel(ChannelAPI *channel);
|
||||
void sendPacket() { m_source.sendPacket(); }
|
||||
|
||||
signals:
|
||||
/**
|
||||
|
@ -182,6 +182,22 @@ void M17ModGUI::on_toneFrequency_valueChanged(int value)
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_fmAudio_toggled(bool checked)
|
||||
{
|
||||
m_fmAudioMode = checked;
|
||||
|
||||
if ((checked) && (m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeM17Audio))
|
||||
{
|
||||
m_settings.m_m17Mode = M17ModSettings::M17Mode::M17ModeFMAudio;
|
||||
applySettings();
|
||||
}
|
||||
else if ((!checked) && (m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeFMAudio))
|
||||
{
|
||||
m_settings.m_m17Mode = M17ModSettings::M17Mode::M17ModeM17Audio;
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModGUI::on_channelMute_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_channelMute = checked;
|
||||
@ -196,9 +212,13 @@ void M17ModGUI::on_playLoop_toggled(bool checked)
|
||||
|
||||
void M17ModGUI::on_play_toggled(bool checked)
|
||||
{
|
||||
ui->tone->setEnabled(!checked); // release other source inputs
|
||||
ui->mic->setEnabled(!checked);
|
||||
m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputFile : M17ModSettings::M17ModInputNone;
|
||||
m_settings.m_audioType = checked ? M17ModSettings::AudioFile : M17ModSettings::AudioNone;
|
||||
m_settings.m_m17Mode = checked ?
|
||||
m_fmAudioMode ?
|
||||
M17ModSettings::M17Mode::M17ModeFMAudio
|
||||
: M17ModSettings::M17Mode::M17ModeM17Audio
|
||||
: M17ModSettings::M17ModeNone;
|
||||
displayModes();
|
||||
applySettings();
|
||||
ui->navTimeSlider->setEnabled(!checked);
|
||||
m_enableNavTime = !checked;
|
||||
@ -206,17 +226,20 @@ void M17ModGUI::on_play_toggled(bool checked)
|
||||
|
||||
void M17ModGUI::on_tone_toggled(bool checked)
|
||||
{
|
||||
ui->play->setEnabled(!checked); // release other source inputs
|
||||
ui->mic->setEnabled(!checked);
|
||||
m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputTone : M17ModSettings::M17ModInputNone;
|
||||
m_settings.m_m17Mode = checked ? M17ModSettings::M17ModeFMTone : M17ModSettings::M17ModeNone;
|
||||
displayModes();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_mic_toggled(bool checked)
|
||||
{
|
||||
ui->play->setEnabled(!checked); // release other source inputs
|
||||
ui->tone->setEnabled(!checked); // release other source inputs
|
||||
m_settings.m_modAFInput = checked ? M17ModSettings::M17ModInputAudio : M17ModSettings::M17ModInputNone;
|
||||
m_settings.m_audioType = checked ? M17ModSettings::AudioInput : M17ModSettings::AudioNone;
|
||||
m_settings.m_m17Mode = checked ?
|
||||
m_fmAudioMode ?
|
||||
M17ModSettings::M17Mode::M17ModeFMAudio
|
||||
: M17ModSettings::M17Mode::M17ModeM17Audio
|
||||
: M17ModSettings::M17ModeNone;
|
||||
displayModes();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
@ -261,6 +284,66 @@ void M17ModGUI::on_showFileDialog_clicked(bool checked)
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModGUI::on_packetMode_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_m17Mode = checked ? M17ModSettings::M17ModeM17Packet : M17ModSettings::M17ModeNone;
|
||||
displayModes();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_sendPacket_clicked(bool)
|
||||
{
|
||||
m_m17Mod->sendPacket();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_loopPacket_toggled(bool checked)
|
||||
{
|
||||
(void) checked;
|
||||
// TODO
|
||||
}
|
||||
|
||||
void M17ModGUI::on_loopPacketInterval_valueChanged(int value)
|
||||
{
|
||||
(void) value;
|
||||
// TODO
|
||||
}
|
||||
|
||||
void M17ModGUI::on_packetDataWidget_currentChanged(int index)
|
||||
{
|
||||
m_settings.m_packetType = indexToPacketType(index);
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_source_editingFinished()
|
||||
{
|
||||
m_settings.m_sourceCall = ui->source->text();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_destination_editingFinished()
|
||||
{
|
||||
m_settings.m_destCall = ui->destination->text();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_insertPosition_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_insertPosition = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_can_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_can = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_smsText_editingFinished()
|
||||
{
|
||||
m_settings.m_smsText = ui->smsText->toPlainText();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::configureFileName()
|
||||
{
|
||||
qDebug() << "M17ModGUI::configureFileName: " << m_fileName.toStdString().c_str();
|
||||
@ -333,6 +416,7 @@ M17ModGUI::M17ModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam
|
||||
m_deviceCenterFrequency(0),
|
||||
m_basebandSampleRate(1),
|
||||
m_doApplySettings(true),
|
||||
m_fmAudioMode(false),
|
||||
m_recordLength(0),
|
||||
m_recordSampleRate(48000),
|
||||
m_samplesCount(0),
|
||||
@ -447,23 +531,101 @@ void M17ModGUI::displaySettings()
|
||||
ui->channelMute->setChecked(m_settings.m_channelMute);
|
||||
ui->playLoop->setChecked(m_settings.m_playLoop);
|
||||
|
||||
ui->tone->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputTone) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone));
|
||||
ui->mic->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputAudio) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone));
|
||||
ui->play->setEnabled((m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputFile) || (m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputNone));
|
||||
|
||||
ui->tone->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputTone);
|
||||
ui->mic->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputAudio);
|
||||
ui->play->setChecked(m_settings.m_modAFInput == M17ModSettings::M17ModInputAF::M17ModInputFile);
|
||||
displayModes();
|
||||
ui->fmAudio->setChecked(m_fmAudioMode);
|
||||
ui->packetDataWidget->setCurrentIndex(packetTypeToIndex(m_settings.m_packetType));
|
||||
|
||||
ui->feedbackEnable->setChecked(m_settings.m_feedbackAudioEnable);
|
||||
ui->feedbackVolume->setValue(roundf(m_settings.m_feedbackVolumeFactor * 100.0));
|
||||
ui->feedbackVolumeText->setText(QString("%1").arg(m_settings.m_feedbackVolumeFactor, 0, 'f', 2));
|
||||
|
||||
ui->source->setText(m_settings.m_sourceCall);
|
||||
ui->destination->setText(m_settings.m_destCall);
|
||||
ui->insertPosition->setChecked(m_settings.m_insertPosition);
|
||||
ui->can->setValue(m_settings.m_can);
|
||||
|
||||
ui->smsText->setText(m_settings.m_smsText);
|
||||
|
||||
ui->aprsFromText->setText(m_settings.m_aprsCallsign);
|
||||
ui->aprsData->setText(m_settings.m_aprsData);
|
||||
ui->aprsTo->lineEdit()->setText(m_settings.m_aprsTo);
|
||||
ui->aprsVia->lineEdit()->setText(m_settings.m_aprsVia);
|
||||
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
updateAbsoluteCenterFrequency();
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void M17ModGUI::displayModes()
|
||||
{
|
||||
qDebug("M17ModGUI::displayModes: m_m17Mode: %d m_audioType: %d",
|
||||
(int) m_settings.m_m17Mode, (int) m_settings.m_audioType);
|
||||
|
||||
if (m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeM17Packet)
|
||||
{
|
||||
ui->packetMode->setChecked(true);
|
||||
ui->packetMode->setEnabled(true);
|
||||
ui->tone->setChecked(false);
|
||||
ui->mic->setChecked(false);
|
||||
ui->play->setChecked(false);
|
||||
ui->tone->setEnabled(false);
|
||||
ui->mic->setEnabled(false);
|
||||
ui->play->setEnabled(false);
|
||||
}
|
||||
else if (m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeFMTone)
|
||||
{
|
||||
ui->tone->setChecked(true);
|
||||
ui->tone->setEnabled(true);
|
||||
ui->packetMode->setChecked(false);
|
||||
ui->mic->setChecked(false);
|
||||
ui->play->setChecked(false);
|
||||
ui->packetMode->setEnabled(false);
|
||||
ui->mic->setEnabled(false);
|
||||
ui->play->setEnabled(false);
|
||||
}
|
||||
else if ((m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeFMAudio) ||
|
||||
(m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeM17Audio))
|
||||
{
|
||||
ui->tone->setChecked(false);
|
||||
ui->packetMode->setChecked(false);
|
||||
ui->tone->setEnabled(false);
|
||||
ui->packetMode->setEnabled(false);
|
||||
|
||||
if (m_settings.m_audioType == M17ModSettings::AudioType::AudioInput)
|
||||
{
|
||||
ui->mic->setChecked(true);
|
||||
ui->mic->setEnabled(true);
|
||||
ui->play->setChecked(false);
|
||||
ui->play->setEnabled(false);
|
||||
}
|
||||
else if (m_settings.m_audioType == M17ModSettings::AudioType::AudioFile)
|
||||
{
|
||||
ui->play->setChecked(true);
|
||||
ui->play->setEnabled(true);
|
||||
ui->mic->setChecked(false);
|
||||
ui->mic->setEnabled(false);
|
||||
}
|
||||
else if (m_settings.m_audioType == M17ModSettings::AudioType::AudioNone)
|
||||
{
|
||||
ui->mic->setChecked(false);
|
||||
ui->play->setChecked(false);
|
||||
ui->mic->setEnabled(true);
|
||||
ui->play->setEnabled(true);
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_m17Mode == M17ModSettings::M17Mode::M17ModeNone)
|
||||
{
|
||||
ui->packetMode->setChecked(false);
|
||||
ui->tone->setChecked(false);
|
||||
ui->mic->setChecked(false);
|
||||
ui->play->setChecked(false);
|
||||
ui->packetMode->setEnabled(true);
|
||||
ui->tone->setEnabled(true);
|
||||
ui->mic->setEnabled(true);
|
||||
ui->play->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModGUI::leaveEvent(QEvent* event)
|
||||
{
|
||||
m_channelMarker.setHighlighted(false);
|
||||
@ -534,7 +696,7 @@ void M17ModGUI::tick()
|
||||
m_feedbackAudioSampleRate = feedbackAudioSampleRate;
|
||||
}
|
||||
|
||||
if (((++m_tickCount & 0xf) == 0) && (m_settings.m_modAFInput == M17ModSettings::M17ModInputFile))
|
||||
if (((++m_tickCount & 0xf) == 0) && (m_settings.m_audioType == M17ModSettings::AudioFile))
|
||||
{
|
||||
M17Mod::MsgConfigureFileSourceStreamTiming* message = M17Mod::MsgConfigureFileSourceStreamTiming::create();
|
||||
m_m17Mod->getInputMessageQueue()->push(message);
|
||||
@ -591,9 +753,41 @@ void M17ModGUI::makeUIConnections()
|
||||
QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &M17ModGUI::on_showFileDialog_clicked);
|
||||
QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &M17ModGUI::on_feedbackEnable_toggled);
|
||||
QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &M17ModGUI::on_feedbackVolume_valueChanged);
|
||||
QObject::connect(ui->fmAudio, &ButtonSwitch::toggled, this, &M17ModGUI::on_fmAudio_toggled);
|
||||
QObject::connect(ui->packetMode, &ButtonSwitch::toggled, this, &M17ModGUI::on_packetMode_toggled);
|
||||
QObject::connect(ui->sendPacket, &QPushButton::clicked, this, &M17ModGUI::on_sendPacket_clicked);
|
||||
QObject::connect(ui->loopPacket, &ButtonSwitch::toggled, this, &M17ModGUI::on_loopPacket_toggled);
|
||||
QObject::connect(ui->loopPacketInterval, &QDial::valueChanged, this, &M17ModGUI::on_loopPacketInterval_valueChanged);
|
||||
QObject::connect(ui->smsText, &CustomTextEdit::editingFinished, this, &M17ModGUI::on_smsText_editingFinished);
|
||||
}
|
||||
|
||||
void M17ModGUI::updateAbsoluteCenterFrequency()
|
||||
{
|
||||
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
|
||||
}
|
||||
|
||||
M17ModSettings::PacketType M17ModGUI::indexToPacketType(int index)
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0:
|
||||
return M17ModSettings::PacketType::PacketSMS;
|
||||
case 1:
|
||||
return M17ModSettings::PacketType::PacketAPRS;
|
||||
default:
|
||||
return M17ModSettings::PacketType::PacketNone;
|
||||
}
|
||||
}
|
||||
|
||||
int M17ModGUI::packetTypeToIndex(M17ModSettings::PacketType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case M17ModSettings::PacketType::PacketSMS:
|
||||
return 0;
|
||||
case M17ModSettings::PacketType::PacketAPRS:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ private:
|
||||
qint64 m_deviceCenterFrequency;
|
||||
int m_basebandSampleRate;
|
||||
bool m_doApplySettings;
|
||||
bool m_fmAudioMode;
|
||||
|
||||
M17Mod* m_m17Mod;
|
||||
MovingAverageUtil<double, double, 20> m_channelPowerDbAvg;
|
||||
@ -88,7 +89,6 @@ private:
|
||||
int m_feedbackAudioSampleRate;
|
||||
std::size_t m_tickCount;
|
||||
bool m_enableNavTime;
|
||||
M17ModSettings::M17ModInputAF m_modAFInput;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
QRegExpValidator m_dcsCodeValidator;
|
||||
|
||||
@ -98,11 +98,14 @@ private:
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displayModes();
|
||||
void updateWithStreamData();
|
||||
void updateWithStreamTime();
|
||||
bool handleMessage(const Message& message);
|
||||
void makeUIConnections();
|
||||
void updateAbsoluteCenterFrequency();
|
||||
M17ModSettings::PacketType indexToPacketType(int index);
|
||||
int packetTypeToIndex(M17ModSettings::PacketType type);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
@ -115,6 +118,7 @@ private slots:
|
||||
void on_rfBW_valueChanged(int value);
|
||||
void on_fmDev_valueChanged(int value);
|
||||
void on_toneFrequency_valueChanged(int value);
|
||||
void on_fmAudio_toggled(bool checked);
|
||||
void on_volume_valueChanged(int value);
|
||||
void on_channelMute_toggled(bool checked);
|
||||
void on_tone_toggled(bool checked);
|
||||
@ -128,6 +132,17 @@ private slots:
|
||||
void on_feedbackEnable_toggled(bool checked);
|
||||
void on_feedbackVolume_valueChanged(int value);
|
||||
|
||||
void on_packetMode_toggled(bool checked);
|
||||
void on_sendPacket_clicked(bool checked);
|
||||
void on_loopPacket_toggled(bool checked);
|
||||
void on_loopPacketInterval_valueChanged(int value);
|
||||
void on_packetDataWidget_currentChanged(int index);
|
||||
void on_source_editingFinished();
|
||||
void on_destination_editingFinished();
|
||||
void on_insertPosition_toggled(bool checked);
|
||||
void on_can_valueChanged(int value);
|
||||
void on_smsText_editingFinished();
|
||||
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>278</height>
|
||||
<height>568</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -40,13 +40,13 @@
|
||||
<property name="windowTitle">
|
||||
<string>M17 Modulator</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsContainer" native="true">
|
||||
<widget class="QWidget" name="aSettingsContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>358</width>
|
||||
<height>271</height>
|
||||
<height>105</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
@ -286,13 +286,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="volumeLayout">
|
||||
<item>
|
||||
@ -364,12 +357,42 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="bAudioContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>107</y>
|
||||
<width>361</width>
|
||||
<height>140</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Audio</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="recordFileSelectLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="tone">
|
||||
<property name="toolTip">
|
||||
<string>FM tone modulation</string>
|
||||
<string>Analog FM tone modulation</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
@ -424,6 +447,22 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="fmAudio">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Modulate audio as analog FM (for testing)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>FM</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="mic">
|
||||
<property name="toolTip">
|
||||
@ -532,13 +571,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="fileNameLayout">
|
||||
<item>
|
||||
@ -704,6 +736,606 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="cPacketContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>247</y>
|
||||
<width>358</width>
|
||||
<height>193</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Packet</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="packetMode">
|
||||
<property name="toolTip">
|
||||
<string>Packet mode</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PKT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sendPacket">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Send packet</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/stream.png</normaloff>:/stream.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="loopPacket">
|
||||
<property name="toolTip">
|
||||
<string>Send packets in a loop</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/playloop.png</normaloff>:/playloop.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="loopPacketInterval">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Interval between packets (s)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>600</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>60</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="loopPacketIntervalText">
|
||||
<property name="toolTip">
|
||||
<string>Interval between packets (s)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>600</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="packetDataWidget">
|
||||
<property name="toolTip">
|
||||
<string>Packet data</string>
|
||||
</property>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::East</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="pktSMS">
|
||||
<property name="toolTip">
|
||||
<string>SMS data</string>
|
||||
</property>
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/sms.png</normaloff>:/sms.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string/>
|
||||
</attribute>
|
||||
<attribute name="toolTip">
|
||||
<string>SMS</string>
|
||||
</attribute>
|
||||
<widget class="CustomTextEdit" name="smsText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>10</y>
|
||||
<width>307</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>SMS text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pktAPRS">
|
||||
<property name="toolTip">
|
||||
<string>APRS data</string>
|
||||
</property>
|
||||
<attribute name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/world.png</normaloff>:/world.png</iconset>
|
||||
</attribute>
|
||||
<attribute name="title">
|
||||
<string/>
|
||||
</attribute>
|
||||
<attribute name="toolTip">
|
||||
<string>APRS</string>
|
||||
</attribute>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>40</y>
|
||||
<width>301</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="toLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="aprsToLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>To</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="aprsTo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter destination</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="currentText">
|
||||
<string>APRS</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>APRS</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>APZ</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CQ</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BEACON</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CALLSIGN-SSID</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="aprsViaLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Via</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="aprsVia">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Enter routing</string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>WIDE2-2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ARISS</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>80</y>
|
||||
<width>301</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="aprsDataLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="aprsDataLabel">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="aprsData">
|
||||
<property name="toolTip">
|
||||
<string>Enter data to transmit.
|
||||
</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>>Using SDRangel</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>256</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>301</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="fromLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>From</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="aprsFromText">
|
||||
<property name="toolTip">
|
||||
<string>Enter your amateur radio callsign and optionally a SSID. E.g. M7RCE or M7RCE-1</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>MYCALL</string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="aprsInsertPosition">
|
||||
<property name="toolTip">
|
||||
<string>Insert position (latitude and longitude)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/gps.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="aprsFromLabel">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="dDigitalContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>442</y>
|
||||
<width>358</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Digital</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="0">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="TVScreen" name="screenTV" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<widget class="QComboBox" name="baudRate">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>60</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Baud rate: 2.4k: NXDN48, dPMR 4.8k: DMR, D-Star, YSF, NXDN96</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4.8k</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QLabel" name="sourceLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>40</y>
|
||||
<width>21</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Src</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="source">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>40</y>
|
||||
<width>100</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="destinationLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>70</y>
|
||||
<width>21</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dst</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="destination">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>70</y>
|
||||
<width>100</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="ButtonSwitch" name="insertPosition">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>150</x>
|
||||
<y>40</y>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Insert position (latitude and longitude)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normalon>:/gps.png</normalon>
|
||||
</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="destinationLabel_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>150</x>
|
||||
<y>70</y>
|
||||
<width>30</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>CAN</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QSpinBox" name="can">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>180</x>
|
||||
<y>70</y>
|
||||
<width>56</width>
|
||||
<height>28</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
@ -723,12 +1355,23 @@
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>TVScreen</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/tvscreen.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LevelMeterVU</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/levelmeter.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>CustomTextEdit</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>gui/customtextedit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
|
99
plugins/channeltx/modm17/m17modprocessor.cpp
Normal file
99
plugins/channeltx/modm17/m17modprocessor.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 "m17/M17Modulator.h"
|
||||
|
||||
#include "m17modprocessor.h"
|
||||
|
||||
M17ModProcessor::M17ModProcessor()
|
||||
{
|
||||
m_basebandFifo.setSampleSize(sizeof(int16_t), 48000);
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
}
|
||||
|
||||
M17ModProcessor::~M17ModProcessor()
|
||||
{}
|
||||
|
||||
bool M17ModProcessor::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgSendSMS::match(cmd))
|
||||
{
|
||||
const MsgSendSMS& notif = (const MsgSendSMS&) cmd;
|
||||
QByteArray packetBytes = notif.getSMSText().toUtf8();
|
||||
packetBytes.prepend(0x05); // SMS standard type
|
||||
packetBytes.truncate(798); // Maximum packet size is 798 payload + 2 bytes CRC = 800 bytes (32*25)
|
||||
processPacket(notif.getSourceCall(), notif.getDestCall(), packetBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void M17ModProcessor::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != nullptr)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModProcessor::processPacket(const QString& sourceCall, const QString& destCall, const QByteArray& packetBytes)
|
||||
{
|
||||
mobilinkd::M17Modulator modulator(sourceCall.toStdString(), destCall.toStdString());
|
||||
// preamble
|
||||
std::array<uint8_t, 48> preamble_bytes;
|
||||
mobilinkd::M17Modulator::make_preamble(preamble_bytes);
|
||||
std::array<int8_t, 48 * 4> fullframe_symbols = mobilinkd::M17Modulator::bytes_to_symbols(preamble_bytes);
|
||||
std::array<int16_t, 1920> baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
// LSF
|
||||
mobilinkd::M17Modulator::lich_t lichSegments; // Not used for packet
|
||||
mobilinkd::M17Modulator::frame_t frame;
|
||||
modulator.make_link_setup(lichSegments, frame);
|
||||
std::array<int8_t, 46 * 4> frame_symbols = mobilinkd::M17Modulator::bytes_to_symbols(frame);
|
||||
std::copy(mobilinkd::M17Modulator::LSF_SYNC_WORD.begin(), mobilinkd::M17Modulator::LSF_SYNC_WORD.end(), fullframe_symbols.begin());
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
// Packets
|
||||
std::copy(mobilinkd::M17Modulator::DATA_SYNC_WORD.begin(), mobilinkd::M17Modulator::DATA_SYNC_WORD.end(), fullframe_symbols.begin());
|
||||
mobilinkd::M17Modulator::packet_t packet;
|
||||
int remainderCount = packetBytes.size();
|
||||
int packetCount = 0;
|
||||
|
||||
while (remainderCount > 25)
|
||||
{
|
||||
std::copy(packetBytes.begin() + (packetCount*25), packetBytes.begin() + ((packetCount+1)*25), packet.begin());
|
||||
frame = modulator.make_packet_frame(packetCount, false, packet, 25);
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
remainderCount -= 25;
|
||||
packetCount++;
|
||||
}
|
||||
|
||||
std::copy(packetBytes.begin() + (packetCount*25), packetBytes.begin() + (packetCount*25) + remainderCount, packet.begin());
|
||||
frame = modulator.make_packet_frame(packetCount, true, packet, remainderCount);
|
||||
std::copy(frame_symbols.begin(), frame_symbols.end(), fullframe_symbols.begin()+2);
|
||||
baseband = mobilinkd::M17Modulator::symbols_to_baseband(fullframe_symbols);
|
||||
m_basebandFifo.write((const quint8*) baseband.data(), 1920);
|
||||
}
|
@ -29,22 +29,28 @@ class M17ModProcessor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgSendPacket : public Message {
|
||||
class MsgSendSMS : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QByteArray& getPacket() const { return m_packet; }
|
||||
const QString& getSourceCall() const { return m_sourceCall; }
|
||||
const QString& getDestCall() const { return m_destCall; }
|
||||
const QString& getSMSText() const { return m_smsText; }
|
||||
|
||||
static MsgSendPacket* create(const QByteArray& packet) {
|
||||
return new MsgSendPacket(packet);
|
||||
static MsgSendSMS* create(const QString& sourceCall, const QString& destCall, const QString& smsText) {
|
||||
return new MsgSendSMS(sourceCall, destCall, smsText);
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_packet;
|
||||
QString m_sourceCall;
|
||||
QString m_destCall;
|
||||
QString m_smsText;
|
||||
|
||||
MsgSendPacket(const QByteArray& bytes) :
|
||||
MsgSendSMS(const QString& sourceCall, const QString& destCall, const QString& smsText) :
|
||||
Message(),
|
||||
m_packet(bytes)
|
||||
m_sourceCall(sourceCall),
|
||||
m_destCall(destCall),
|
||||
m_smsText(smsText)
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -59,6 +65,7 @@ private:
|
||||
AudioFifo m_basebandFifo; //!< Samples are 16 bit integer baseband 48 kS/s samples
|
||||
|
||||
bool handleMessage(const Message& cmd);
|
||||
void processPacket(const QString& sourceCall, const QString& destCall, const QByteArray& packetBytes);
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
|
@ -42,7 +42,9 @@ void M17ModSettings::resetToDefaults()
|
||||
m_playLoop = false;
|
||||
m_rgbColor = QColor(255, 0, 255).rgb();
|
||||
m_title = "M17 Modulator";
|
||||
m_modAFInput = M17ModInputAF::M17ModInputNone;
|
||||
m_m17Mode = M17Mode::M17ModeNone;
|
||||
m_audioType = AudioType::AudioNone;
|
||||
m_packetType = PacketType::PacketNone;
|
||||
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_feedbackAudioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_feedbackVolumeFactor = 0.5f;
|
||||
@ -55,6 +57,16 @@ void M17ModSettings::resetToDefaults()
|
||||
m_reverseAPIChannelIndex = 0;
|
||||
m_workspaceIndex = 0;
|
||||
m_hidden = false;
|
||||
m_sourceCall = "";
|
||||
m_destCall = "";
|
||||
m_insertPosition = false;
|
||||
m_can = 10;
|
||||
m_smsText = "";
|
||||
m_aprsCallsign = "MYCALL";
|
||||
m_aprsTo = "APRS";
|
||||
m_aprsVia = "WIDE2-2";
|
||||
m_aprsData = ">Using SDRangel";
|
||||
m_aprsInsertPosition = 0;
|
||||
}
|
||||
|
||||
QByteArray M17ModSettings::serialize() const
|
||||
@ -67,13 +79,15 @@ QByteArray M17ModSettings::serialize() const
|
||||
s.writeU32(5, m_rgbColor);
|
||||
s.writeReal(6, m_toneFrequency);
|
||||
s.writeReal(7, m_volumeFactor);
|
||||
s.writeS32(8, (int) m_m17Mode);
|
||||
s.writeS32(9, (int) m_audioType);
|
||||
s.writeS32(10, (int) m_packetType);
|
||||
|
||||
if (m_channelMarker) {
|
||||
s.writeBlob(11, m_channelMarker->serialize());
|
||||
}
|
||||
|
||||
s.writeString(12, m_title);
|
||||
s.writeS32(13, (int) m_modAFInput);
|
||||
s.writeString(14, m_audioDeviceName);
|
||||
s.writeBool(15, m_useReverseAPI);
|
||||
s.writeString(16, m_reverseAPIAddress);
|
||||
@ -93,6 +107,19 @@ QByteArray M17ModSettings::serialize() const
|
||||
s.writeBlob(29, m_geometryBytes);
|
||||
s.writeBool(30, m_hidden);
|
||||
|
||||
s.writeString(40, m_sourceCall);
|
||||
s.writeString(41, m_destCall);
|
||||
s.writeBool(42, m_insertPosition);
|
||||
s.writeU32(43, m_can);
|
||||
|
||||
s.writeString(50, m_smsText);
|
||||
|
||||
s.writeString(60, m_aprsCallsign);
|
||||
s.writeString(61, m_aprsTo);
|
||||
s.writeString(62, m_aprsVia);
|
||||
s.writeString(63, m_aprsData);
|
||||
s.writeBool(64, m_aprsInsertPosition);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
@ -119,7 +146,13 @@ bool M17ModSettings::deserialize(const QByteArray& data)
|
||||
d.readU32(5, &m_rgbColor);
|
||||
d.readReal(6, &m_toneFrequency, 1000.0);
|
||||
d.readReal(7, &m_volumeFactor, 1.0);
|
||||
d.readBlob(8, &bytetmp);
|
||||
d.readS32(8, &tmp, 0);
|
||||
m_m17Mode = tmp < 0 ? M17ModeNone : tmp > (int) M17ModeM17BERT ? M17ModeM17BERT : (M17Mode) tmp;
|
||||
d.readS32(9, &tmp, 0);
|
||||
m_audioType = tmp < 0 ? AudioNone : tmp > (int) AudioInput ? AudioInput : (AudioType) tmp;
|
||||
m_packetType = tmp < 0 ? PacketNone : tmp > (int) PacketSMS ? PacketSMS : (PacketType) tmp;
|
||||
|
||||
d.readBlob(11, &bytetmp);
|
||||
|
||||
if (m_channelMarker)
|
||||
{
|
||||
@ -129,13 +162,6 @@ bool M17ModSettings::deserialize(const QByteArray& data)
|
||||
|
||||
d.readString(12, &m_title, "M17 Modulator");
|
||||
|
||||
d.readS32(13, &tmp, 0);
|
||||
if ((tmp < 0) || (tmp > (int) M17ModInputAF::M17ModInputTone)) {
|
||||
m_modAFInput = M17ModInputNone;
|
||||
} else {
|
||||
m_modAFInput = (M17ModInputAF) tmp;
|
||||
}
|
||||
|
||||
d.readString(14, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
|
||||
d.readBool(15, &m_useReverseAPI, false);
|
||||
d.readString(16, &m_reverseAPIAddress, "127.0.0.1");
|
||||
@ -167,6 +193,20 @@ bool M17ModSettings::deserialize(const QByteArray& data)
|
||||
d.readBlob(29, &m_geometryBytes);
|
||||
d.readBool(30, &m_hidden, false);
|
||||
|
||||
d.readString(40, &m_sourceCall, "");
|
||||
d.readString(41, &m_destCall, "");
|
||||
d.readBool(42, &m_insertPosition, false);
|
||||
d.readU32(43, &utmp);
|
||||
m_can = utmp < 255 ? utmp : 255;
|
||||
|
||||
d.readString(50, &m_smsText, "");
|
||||
|
||||
d.readString(60, &m_aprsCallsign, "MYCALL");
|
||||
d.readString(61, &m_aprsTo, "");
|
||||
d.readString(62, &m_aprsVia, "");
|
||||
d.readString(63, &m_aprsData, "");
|
||||
d.readBool(64, &m_aprsInsertPosition, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -26,12 +26,28 @@ class Serializable;
|
||||
|
||||
struct M17ModSettings
|
||||
{
|
||||
enum M17ModInputAF
|
||||
enum M17Mode
|
||||
{
|
||||
M17ModInputNone,
|
||||
M17ModInputFile,
|
||||
M17ModInputAudio,
|
||||
M17ModInputTone
|
||||
M17ModeNone,
|
||||
M17ModeFMTone,
|
||||
M17ModeFMAudio,
|
||||
M17ModeM17Audio,
|
||||
M17ModeM17Packet,
|
||||
M17ModeM17BERT
|
||||
};
|
||||
|
||||
enum AudioType
|
||||
{
|
||||
AudioNone,
|
||||
AudioFile,
|
||||
AudioInput
|
||||
};
|
||||
|
||||
enum PacketType
|
||||
{
|
||||
PacketNone,
|
||||
PacketSMS,
|
||||
PacketAPRS
|
||||
};
|
||||
|
||||
qint64 m_inputFrequencyOffset;
|
||||
@ -43,7 +59,9 @@ struct M17ModSettings
|
||||
bool m_playLoop;
|
||||
quint32 m_rgbColor;
|
||||
QString m_title;
|
||||
M17ModInputAF m_modAFInput;
|
||||
M17Mode m_m17Mode;
|
||||
AudioType m_audioType;
|
||||
PacketType m_packetType;
|
||||
QString m_audioDeviceName; //!< This is the audio device you get the audio samples from
|
||||
QString m_feedbackAudioDeviceName; //!< This is the audio device you send the audio samples to for audio feedback
|
||||
float m_feedbackVolumeFactor;
|
||||
@ -58,6 +76,19 @@ struct M17ModSettings
|
||||
QByteArray m_geometryBytes;
|
||||
bool m_hidden;
|
||||
|
||||
QString m_sourceCall;
|
||||
QString m_destCall;
|
||||
bool m_insertPosition;
|
||||
uint8_t m_can;
|
||||
|
||||
QString m_smsText;
|
||||
|
||||
QString m_aprsCallsign;
|
||||
QString m_aprsTo;
|
||||
QString m_aprsVia;
|
||||
QString m_aprsData;
|
||||
bool m_aprsInsertPosition;
|
||||
|
||||
Serializable *m_channelMarker;
|
||||
Serializable *m_rollupState;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "util/messagequeue.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "m17modprocessor.h"
|
||||
#include "m17modsource.h"
|
||||
|
||||
const int M17ModSource::m_levelNbSamples = 480; // every 10ms
|
||||
@ -53,12 +54,15 @@ M17ModSource::M17ModSource() :
|
||||
|
||||
m_magsq = 0.0;
|
||||
|
||||
m_processor = new M17ModProcessor();
|
||||
|
||||
applySettings(m_settings, true);
|
||||
applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true);
|
||||
}
|
||||
|
||||
M17ModSource::~M17ModSource()
|
||||
{
|
||||
delete m_processor;
|
||||
}
|
||||
|
||||
void M17ModSource::pull(SampleVector::iterator begin, unsigned int nbSamples)
|
||||
@ -140,25 +144,40 @@ void M17ModSource::pullAudio(unsigned int nbSamplesAudio)
|
||||
void M17ModSource::modulateSample()
|
||||
{
|
||||
Real t1, t;
|
||||
bool carrier;
|
||||
|
||||
pullAF(t);
|
||||
if ((m_settings.m_m17Mode == M17ModSettings::M17ModeFMTone) || (m_settings.m_m17Mode == M17ModSettings::M17ModeFMAudio)) {
|
||||
pullAF(t, carrier);
|
||||
} else if (m_settings.m_m17Mode != M17ModSettings::M17ModeNone) {
|
||||
pullM17(t, carrier);
|
||||
} else {
|
||||
t = 0;
|
||||
}
|
||||
|
||||
if (m_settings.m_feedbackAudioEnable) {
|
||||
pushFeedback(t * m_settings.m_feedbackVolumeFactor * 16384.0f);
|
||||
}
|
||||
|
||||
calculateLevel(t);
|
||||
t1 = m_lowpass.filter(t) * 1.2f;
|
||||
if (carrier)
|
||||
{
|
||||
calculateLevel(t);
|
||||
t1 = m_lowpass.filter(t) * 1.2f;
|
||||
|
||||
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * t1;
|
||||
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * t1;
|
||||
|
||||
// limit phasor range to ]-pi,pi]
|
||||
if (m_modPhasor > M_PI) {
|
||||
m_modPhasor -= (2.0f * M_PI);
|
||||
// limit phasor range to ]-pi,pi]
|
||||
if (m_modPhasor > M_PI) {
|
||||
m_modPhasor -= (2.0f * M_PI);
|
||||
}
|
||||
|
||||
m_modSample.real(cos(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); // -1 dB
|
||||
m_modSample.imag(sin(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_modSample.real(0.0f);
|
||||
m_modSample.imag(0.0f);
|
||||
}
|
||||
|
||||
m_modSample.real(cos(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF); // -1 dB
|
||||
m_modSample.imag(sin(m_modPhasor) * 0.891235351562f * SDR_TX_SCALEF);
|
||||
|
||||
m_demodBuffer[m_demodBufferFill] = t1 * std::numeric_limits<int16_t>::max();
|
||||
++m_demodBufferFill;
|
||||
@ -186,63 +205,74 @@ void M17ModSource::modulateSample()
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModSource::pullAF(Real& sample)
|
||||
void M17ModSource::pullAF(Real& sample, bool& carrier)
|
||||
{
|
||||
switch (m_settings.m_modAFInput)
|
||||
{
|
||||
case M17ModSettings::M17ModInputTone:
|
||||
sample = m_toneNco.next();
|
||||
break;
|
||||
case M17ModSettings::M17ModInputFile:
|
||||
// sox f4exb_call.wav --encoding float --endian little f4exb_call.raw
|
||||
// ffplay -f f32le -ar 48k -ac 1 f4exb_call.raw
|
||||
if (m_ifstream && m_ifstream->is_open())
|
||||
{
|
||||
if (m_ifstream->eof())
|
||||
{
|
||||
if (m_settings.m_playLoop)
|
||||
{
|
||||
m_ifstream->clear();
|
||||
m_ifstream->seekg(0, std::ios::beg);
|
||||
}
|
||||
}
|
||||
carrier = true;
|
||||
|
||||
if (m_ifstream->eof())
|
||||
if (m_settings.m_m17Mode == M17ModSettings::M17ModeFMTone)
|
||||
{
|
||||
sample = m_toneNco.next();
|
||||
}
|
||||
else if (m_settings.m_m17Mode == M17ModSettings::M17ModeFMAudio)
|
||||
{
|
||||
if (m_settings.m_audioType == M17ModSettings::AudioFile)
|
||||
{
|
||||
// sox f4exb_call.wav --encoding float --endian little f4exb_call.raw
|
||||
// ffplay -f f32le -ar 48k -ac 1 f4exb_call.raw
|
||||
if (m_ifstream && m_ifstream->is_open())
|
||||
{
|
||||
sample = 0.0f;
|
||||
if (m_ifstream->eof())
|
||||
{
|
||||
if (m_settings.m_playLoop)
|
||||
{
|
||||
m_ifstream->clear();
|
||||
m_ifstream->seekg(0, std::ios::beg);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ifstream->eof())
|
||||
{
|
||||
sample = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ifstream->read(reinterpret_cast<char*>(&sample), sizeof(Real));
|
||||
sample *= m_settings.m_volumeFactor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ifstream->read(reinterpret_cast<char*>(&sample), sizeof(Real));
|
||||
sample *= m_settings.m_volumeFactor;
|
||||
sample = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_audioType == M17ModSettings::AudioInput)
|
||||
{
|
||||
if (m_audioBufferFill < m_audioBuffer.size())
|
||||
{
|
||||
sample = ((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_settings.m_volumeFactor;
|
||||
m_audioBufferFill++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int size = m_audioBuffer.size();
|
||||
qDebug("NFMModSource::pullAF: starve audio samples: size: %u", size);
|
||||
sample = ((m_audioBuffer[size-1].l + m_audioBuffer[size-1].r) / 65536.0f) * m_settings.m_volumeFactor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sample = 0.0f;
|
||||
}
|
||||
break;
|
||||
case M17ModSettings::M17ModInputAudio:
|
||||
if (m_audioBufferFill < m_audioBuffer.size())
|
||||
{
|
||||
sample = ((m_audioBuffer[m_audioBufferFill].l + m_audioBuffer[m_audioBufferFill].r) / 65536.0f) * m_settings.m_volumeFactor;
|
||||
m_audioBufferFill++;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int size = m_audioBuffer.size();
|
||||
qDebug("NFMModSource::pullAF: starve audio samples: size: %u", size);
|
||||
sample = ((m_audioBuffer[size-1].l + m_audioBuffer[size-1].r) / 65536.0f) * m_settings.m_volumeFactor;
|
||||
}
|
||||
|
||||
break;
|
||||
case M17ModSettings::M17ModInputNone:
|
||||
default:
|
||||
sample = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModSource::pullM17(Real& sample, bool& carrier)
|
||||
{
|
||||
// TODO
|
||||
carrier = false;
|
||||
sample = 0.0f;
|
||||
}
|
||||
|
||||
void M17ModSource::pushFeedback(Real sample)
|
||||
{
|
||||
Complex c(sample, sample);
|
||||
@ -368,9 +398,9 @@ void M17ModSource::applySettings(const M17ModSettings& settings, bool force)
|
||||
m_toneNco.setFreq(settings.m_toneFrequency, m_audioSampleRate);
|
||||
}
|
||||
|
||||
if ((settings.m_modAFInput != m_settings.m_modAFInput) || force)
|
||||
if ((settings.m_audioType != m_settings.m_audioType) || force)
|
||||
{
|
||||
if (settings.m_modAFInput == M17ModSettings::M17ModInputAudio) {
|
||||
if (settings.m_audioType == M17ModSettings::AudioInput) {
|
||||
connect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio()));
|
||||
} else {
|
||||
disconnect(&m_audioFifo, SIGNAL(dataReady()), this, SLOT(handleAudio()));
|
||||
@ -416,3 +446,16 @@ void M17ModSource::handleAudio()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void M17ModSource::sendPacket()
|
||||
{
|
||||
if (m_settings.m_packetType == M17ModSettings::PacketType::PacketSMS)
|
||||
{
|
||||
M17ModProcessor::MsgSendSMS *msg = M17ModProcessor::MsgSendSMS::create(
|
||||
m_settings.m_sourceCall,
|
||||
m_settings.m_destCall,
|
||||
m_settings.m_smsText
|
||||
);
|
||||
m_processor->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "m17modsettings.h"
|
||||
|
||||
class ChannelAPI;
|
||||
class M17ModProcessor;
|
||||
|
||||
class M17ModSource : public QObject, public ChannelSampleSource
|
||||
{
|
||||
@ -67,6 +68,8 @@ public:
|
||||
void applySettings(const M17ModSettings& settings, bool force = false);
|
||||
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
|
||||
|
||||
void sendPacket();
|
||||
|
||||
private:
|
||||
int m_channelSampleRate;
|
||||
int m_channelFrequencyOffset;
|
||||
@ -115,6 +118,7 @@ private:
|
||||
Real m_levelSum;
|
||||
|
||||
std::ifstream *m_ifstream;
|
||||
M17ModProcessor *m_processor;
|
||||
|
||||
QMutex m_mutex;
|
||||
|
||||
@ -122,7 +126,8 @@ private:
|
||||
static const float m_preemphasis;
|
||||
|
||||
void processOneSample(Complex& ci);
|
||||
void pullAF(Real& sample);
|
||||
void pullAF(Real& sample, bool& carrier);
|
||||
void pullM17(Real& sample, bool& carrier);
|
||||
void pullAudio(unsigned int nbSamples);
|
||||
void pushFeedback(Real sample);
|
||||
void calculateLevel(Real& sample);
|
||||
|
@ -8390,8 +8390,17 @@ margin-bottom: 20px;
|
||||
"audioDeviceName" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"modAFInput" : {
|
||||
"type" : "integer"
|
||||
"m17Mode" : {
|
||||
"type" : "integer",
|
||||
"description" : "M17Mode"
|
||||
},
|
||||
"audioType" : {
|
||||
"type" : "integer",
|
||||
"description" : "AudioType"
|
||||
},
|
||||
"packetType" : {
|
||||
"type" : "integer",
|
||||
"description" : "PacketType"
|
||||
},
|
||||
"streamIndex" : {
|
||||
"type" : "integer",
|
||||
@ -56380,7 +56389,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2022-06-09T22:25:54.513+02:00
|
||||
Generated 2022-06-10T22:26:56.056+02:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,8 +26,15 @@ M17ModSettings:
|
||||
type: string
|
||||
audioDeviceName:
|
||||
type: string
|
||||
modAFInput:
|
||||
m17Mode:
|
||||
type: integer
|
||||
description: M17Mode
|
||||
audioType:
|
||||
type: integer
|
||||
description: AudioType
|
||||
packetType:
|
||||
type: integer
|
||||
description: PacketType
|
||||
streamIndex:
|
||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
type: integer
|
||||
|
@ -8,6 +8,8 @@
|
||||
<file>bell_fill.png</file>
|
||||
<file>bell_gradient.png</file>
|
||||
<file>bell_line.png</file>
|
||||
<file>world.png</file>
|
||||
<file>sms.png</file>
|
||||
<file>ruler.png</file>
|
||||
<file>sort.png</file>
|
||||
<file>audio_mic.png</file>
|
||||
|
BIN
sdrgui/resources/sms.png
Normal file
BIN
sdrgui/resources/sms.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 821 B |
BIN
sdrgui/resources/world.png
Normal file
BIN
sdrgui/resources/world.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
@ -26,8 +26,15 @@ M17ModSettings:
|
||||
type: string
|
||||
audioDeviceName:
|
||||
type: string
|
||||
modAFInput:
|
||||
m17Mode:
|
||||
type: integer
|
||||
description: M17Mode
|
||||
audioType:
|
||||
type: integer
|
||||
description: AudioType
|
||||
packetType:
|
||||
type: integer
|
||||
description: PacketType
|
||||
streamIndex:
|
||||
description: MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
type: integer
|
||||
|
@ -8390,8 +8390,17 @@ margin-bottom: 20px;
|
||||
"audioDeviceName" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"modAFInput" : {
|
||||
"type" : "integer"
|
||||
"m17Mode" : {
|
||||
"type" : "integer",
|
||||
"description" : "M17Mode"
|
||||
},
|
||||
"audioType" : {
|
||||
"type" : "integer",
|
||||
"description" : "AudioType"
|
||||
},
|
||||
"packetType" : {
|
||||
"type" : "integer",
|
||||
"description" : "PacketType"
|
||||
},
|
||||
"streamIndex" : {
|
||||
"type" : "integer",
|
||||
@ -56380,7 +56389,7 @@ except ApiException as e:
|
||||
</div>
|
||||
<div id="generator">
|
||||
<div class="content">
|
||||
Generated 2022-06-09T22:25:54.513+02:00
|
||||
Generated 2022-06-10T22:26:56.056+02:00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,8 +48,12 @@ SWGM17ModSettings::SWGM17ModSettings() {
|
||||
m_title_isSet = false;
|
||||
audio_device_name = nullptr;
|
||||
m_audio_device_name_isSet = false;
|
||||
mod_af_input = 0;
|
||||
m_mod_af_input_isSet = false;
|
||||
m17_mode = 0;
|
||||
m_m17_mode_isSet = false;
|
||||
audio_type = 0;
|
||||
m_audio_type_isSet = false;
|
||||
packet_type = 0;
|
||||
m_packet_type_isSet = false;
|
||||
stream_index = 0;
|
||||
m_stream_index_isSet = false;
|
||||
use_reverse_api = 0;
|
||||
@ -94,8 +98,12 @@ SWGM17ModSettings::init() {
|
||||
m_title_isSet = false;
|
||||
audio_device_name = new QString("");
|
||||
m_audio_device_name_isSet = false;
|
||||
mod_af_input = 0;
|
||||
m_mod_af_input_isSet = false;
|
||||
m17_mode = 0;
|
||||
m_m17_mode_isSet = false;
|
||||
audio_type = 0;
|
||||
m_audio_type_isSet = false;
|
||||
packet_type = 0;
|
||||
m_packet_type_isSet = false;
|
||||
stream_index = 0;
|
||||
m_stream_index_isSet = false;
|
||||
use_reverse_api = 0;
|
||||
@ -133,6 +141,8 @@ SWGM17ModSettings::cleanup() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(reverse_api_address != nullptr) {
|
||||
delete reverse_api_address;
|
||||
}
|
||||
@ -178,7 +188,11 @@ SWGM17ModSettings::fromJsonObject(QJsonObject &pJson) {
|
||||
|
||||
::SWGSDRangel::setValue(&audio_device_name, pJson["audioDeviceName"], "QString", "QString");
|
||||
|
||||
::SWGSDRangel::setValue(&mod_af_input, pJson["modAFInput"], "qint32", "");
|
||||
::SWGSDRangel::setValue(&m17_mode, pJson["m17Mode"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&audio_type, pJson["audioType"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&packet_type, pJson["packetType"], "qint32", "");
|
||||
|
||||
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
|
||||
|
||||
@ -242,8 +256,14 @@ SWGM17ModSettings::asJsonObject() {
|
||||
if(audio_device_name != nullptr && *audio_device_name != QString("")){
|
||||
toJsonValue(QString("audioDeviceName"), audio_device_name, obj, QString("QString"));
|
||||
}
|
||||
if(m_mod_af_input_isSet){
|
||||
obj->insert("modAFInput", QJsonValue(mod_af_input));
|
||||
if(m_m17_mode_isSet){
|
||||
obj->insert("m17Mode", QJsonValue(m17_mode));
|
||||
}
|
||||
if(m_audio_type_isSet){
|
||||
obj->insert("audioType", QJsonValue(audio_type));
|
||||
}
|
||||
if(m_packet_type_isSet){
|
||||
obj->insert("packetType", QJsonValue(packet_type));
|
||||
}
|
||||
if(m_stream_index_isSet){
|
||||
obj->insert("streamIndex", QJsonValue(stream_index));
|
||||
@ -374,13 +394,33 @@ SWGM17ModSettings::setAudioDeviceName(QString* audio_device_name) {
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGM17ModSettings::getModAfInput() {
|
||||
return mod_af_input;
|
||||
SWGM17ModSettings::getM17Mode() {
|
||||
return m17_mode;
|
||||
}
|
||||
void
|
||||
SWGM17ModSettings::setModAfInput(qint32 mod_af_input) {
|
||||
this->mod_af_input = mod_af_input;
|
||||
this->m_mod_af_input_isSet = true;
|
||||
SWGM17ModSettings::setM17Mode(qint32 m17_mode) {
|
||||
this->m17_mode = m17_mode;
|
||||
this->m_m17_mode_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGM17ModSettings::getAudioType() {
|
||||
return audio_type;
|
||||
}
|
||||
void
|
||||
SWGM17ModSettings::setAudioType(qint32 audio_type) {
|
||||
this->audio_type = audio_type;
|
||||
this->m_audio_type_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
SWGM17ModSettings::getPacketType() {
|
||||
return packet_type;
|
||||
}
|
||||
void
|
||||
SWGM17ModSettings::setPacketType(qint32 packet_type) {
|
||||
this->packet_type = packet_type;
|
||||
this->m_packet_type_isSet = true;
|
||||
}
|
||||
|
||||
qint32
|
||||
@ -498,7 +538,13 @@ SWGM17ModSettings::isSet(){
|
||||
if(audio_device_name && *audio_device_name != QString("")){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_mod_af_input_isSet){
|
||||
if(m_m17_mode_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_audio_type_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_packet_type_isSet){
|
||||
isObjectUpdated = true; break;
|
||||
}
|
||||
if(m_stream_index_isSet){
|
||||
|
@ -74,8 +74,14 @@ public:
|
||||
QString* getAudioDeviceName();
|
||||
void setAudioDeviceName(QString* audio_device_name);
|
||||
|
||||
qint32 getModAfInput();
|
||||
void setModAfInput(qint32 mod_af_input);
|
||||
qint32 getM17Mode();
|
||||
void setM17Mode(qint32 m17_mode);
|
||||
|
||||
qint32 getAudioType();
|
||||
void setAudioType(qint32 audio_type);
|
||||
|
||||
qint32 getPacketType();
|
||||
void setPacketType(qint32 packet_type);
|
||||
|
||||
qint32 getStreamIndex();
|
||||
void setStreamIndex(qint32 stream_index);
|
||||
@ -135,8 +141,14 @@ private:
|
||||
QString* audio_device_name;
|
||||
bool m_audio_device_name_isSet;
|
||||
|
||||
qint32 mod_af_input;
|
||||
bool m_mod_af_input_isSet;
|
||||
qint32 m17_mode;
|
||||
bool m_m17_mode_isSet;
|
||||
|
||||
qint32 audio_type;
|
||||
bool m_audio_type_isSet;
|
||||
|
||||
qint32 packet_type;
|
||||
bool m_packet_type_isSet;
|
||||
|
||||
qint32 stream_index;
|
||||
bool m_stream_index_isSet;
|
||||
|
Loading…
Reference in New Issue
Block a user