diff --git a/Conf.cpp b/Conf.cpp index d69d29751..83d22f4d1 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -283,12 +283,16 @@ m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), m_fmNetworkEnabled(false), -m_fmNetworkFormat("MMDVM"), +m_fmNetworkProtocol("MMDVM"), m_fmGatewayAddress(), m_fmGatewayPort(0U), m_fmLocalAddress(), m_fmLocalPort(0U), m_fmSampleRate(8000U), +m_fmPreEmphasis(true), +m_fmDeEmphasis(true), +m_fmTXAudioGain(1.0F), +m_fmRXAudioGain(1.0F), m_fmNetworkModeHang(3U), m_fmNetworkDebug(false), m_ax25NetworkEnabled(false), @@ -999,8 +1003,8 @@ bool CConf::read() } else if (section == SECTION_FM_NETWORK) { if (::strcmp(key, "Enable") == 0) m_fmNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Format") == 0) - m_fmNetworkFormat = value; + else if (::strcmp(key, "Protocol") == 0) + m_fmNetworkProtocol = value; else if (::strcmp(key, "LocalAddress") == 0) m_fmLocalAddress = value; else if (::strcmp(key, "LocalPort") == 0) @@ -1011,6 +1015,14 @@ bool CConf::read() m_fmGatewayPort = (unsigned int)::atoi(value); else if (::strcmp(key, "SampleRate") == 0) m_fmSampleRate = (unsigned int)::atoi(value); + else if (::strcmp(key, "PreEmphasis") == 0) + m_fmPreEmphasis = ::atoi(value) == 1; + else if (::strcmp(key, "DeEmphasis") == 0) + m_fmDeEmphasis = ::atoi(value) == 1; + else if (::strcmp(key, "TXAudioGain") == 0) + m_fmTXAudioGain = float(::atof(value)); + else if (::strcmp(key, "RXAudioGain") == 0) + m_fmRXAudioGain = float(::atof(value)); else if (::strcmp(key, "ModeHang") == 0) m_fmNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) @@ -2200,9 +2212,9 @@ bool CConf::getFMNetworkEnabled() const return m_fmNetworkEnabled; } -std::string CConf::getFMNetworkFormat() const +std::string CConf::getFMNetworkProtocol() const { - return m_fmNetworkFormat; + return m_fmNetworkProtocol; } std::string CConf::getFMGatewayAddress() const @@ -2230,6 +2242,26 @@ unsigned int CConf::getFMSampleRate() const return m_fmSampleRate; } +bool CConf::getFMPreEmphasis() const +{ + return m_fmPreEmphasis; +} + +bool CConf::getFMDeEmphasis() const +{ + return m_fmDeEmphasis; +} + +float CConf::getFMTXAudioGain() const +{ + return m_fmTXAudioGain; +} + +float CConf::getFMRXAudioGain() const +{ + return m_fmRXAudioGain; +} + unsigned int CConf::getFMNetworkModeHang() const { return m_fmNetworkModeHang; diff --git a/Conf.h b/Conf.h index 7a5c56a4f..aad115581 100644 --- a/Conf.h +++ b/Conf.h @@ -296,12 +296,16 @@ class CConf // The FM Network section bool getFMNetworkEnabled() const; - std::string getFMNetworkFormat() const; + std::string getFMNetworkProtocol() const; std::string getFMGatewayAddress() const; unsigned int getFMGatewayPort() const; std::string getFMLocalAddress() const; unsigned int getFMLocalPort() const; unsigned int getFMSampleRate() const; + bool getFMPreEmphasis() const; + bool getFMDeEmphasis() const; + float getFMTXAudioGain() const; + float getFMRXAudioGain() const; unsigned int getFMNetworkModeHang() const; bool getFMNetworkDebug() const; @@ -604,12 +608,16 @@ class CConf bool m_pocsagNetworkDebug; bool m_fmNetworkEnabled; - std::string m_fmNetworkFormat; + std::string m_fmNetworkProtocol; std::string m_fmGatewayAddress; unsigned int m_fmGatewayPort; std::string m_fmLocalAddress; unsigned int m_fmLocalPort; unsigned int m_fmSampleRate; + bool m_fmPreEmphasis; + bool m_fmDeEmphasis; + float m_fmTXAudioGain; + float m_fmRXAudioGain; unsigned int m_fmNetworkModeHang; bool m_fmNetworkDebug; diff --git a/FMControl.cpp b/FMControl.cpp index 076cbc9d9..c44c3247e 100644 --- a/FMControl.cpp +++ b/FMControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX * * 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 @@ -31,20 +31,27 @@ const float PREEMPHASIS_GAIN_DB = 0.0F; // Audio gain adjustment const float FILTER_GAIN_DB = 2.0F; // Audio gain adjustment const unsigned int FM_MASK = 0x00000FFFU; -CFMControl::CFMControl(CFMNetwork* network) : +CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn) : m_network(network), +m_txAudioGain(txAudioGain), +m_rxAudioGain(rxAudioGain), +m_preEmphasisOn(preEmphasisOn), +m_deEmphasisOn(deEmphasisOn), m_enabled(false), m_incomingRFAudio(1600U, "Incoming RF FM Audio"), -m_preemphasis (NULL), -m_deemphasis (NULL), +m_preEmphasis(NULL), +m_deEmphasis(NULL), m_filterStage1(NULL), m_filterStage2(NULL), m_filterStage3(NULL) { - m_preemphasis = new CIIRDirectForm1Filter(8.315375384336983F,-7.03334621603483F,0.0F,1.0F,0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); - m_deemphasis = new CIIRDirectForm1Filter(0.07708787090460224F,0.07708787090460224F,0.0F,1.0F,-0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); + assert(txAudioGain > 0.0F); + assert(rxAudioGain > 0.0F); - //cheby type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 + m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); + m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); + + // Chebyshev type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB); m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB); m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB); @@ -52,8 +59,8 @@ m_filterStage3(NULL) CFMControl::~CFMControl() { - delete m_preemphasis ; - delete m_deemphasis ; + delete m_preEmphasis; + delete m_deEmphasis; delete m_filterStage1; delete m_filterStage2; @@ -79,42 +86,45 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length) m_incomingRFAudio.addData(data + 1U, length - 1U); unsigned int bufferLength = m_incomingRFAudio.dataSize(); - if (bufferLength > 240U) //160 samples 12-bit - bufferLength = 240U; //160 samples 12-bit + if (bufferLength > 240U) // 160 samples 12-bit + bufferLength = 240U; // 160 samples 12-bit if (bufferLength >= 3U) { - bufferLength = bufferLength - bufferLength % 3U; //round down to nearest multiple of 3 - unsigned char bufferData[240U]; //160 samples 12-bit + bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3 + unsigned char bufferData[240U]; // 160 samples 12-bit m_incomingRFAudio.getData(bufferData, bufferLength); unsigned int pack = 0U; unsigned char* packPointer = (unsigned char*)&pack; - float out[160U]; //160 samples 12-bit + float out[160U]; // 160 samples 12-bit unsigned int nOut = 0U; short unpackedSamples[2U]; for (unsigned int i = 0U; i < bufferLength; i += 3U) { - //extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed - packPointer[0U] = bufferData[i]; + // Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed + packPointer[0U] = bufferData[i + 0U]; packPointer[1U] = bufferData[i + 1U]; packPointer[2U] = bufferData[i + 2U]; + unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048); unpackedSamples[0U] = short(int(pack >> 12 & FM_MASK) - 2048); // - //process unpacked sample pair + // Process unpacked sample pair for (unsigned char j = 0U; j < 2U; j++) { // Convert to float (-1.0 to +1.0) - float sampleFloat = float(unpackedSamples[j]) / 2048.0F; + float sampleFloat = (float(unpackedSamples[j]) * m_rxAudioGain) / 2048.0F; // De-emphasise and remove CTCSS - sampleFloat = m_deemphasis->filter(sampleFloat); + if (m_deEmphasisOn) + sampleFloat = m_deEmphasis->filter(sampleFloat); + out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat))); } } #if defined(DUMP_RF_AUDIO) FILE * audiofile = fopen("./audiodump.bin", "ab"); - if(audiofile != NULL) { + if (audiofile != NULL) { fwrite(out, sizeof(float), nOut, audiofile); fclose(audiofile); } @@ -133,8 +143,8 @@ unsigned int CFMControl::readModem(unsigned char* data, unsigned int space) if (m_network == NULL) return 0U; - if (space > 240U) //160 samples 12-bit - space = 240U; //160 samples 12-bit + if (space > 240U) // 160 samples 12-bit + space = 240U; // 160 samples 12-bit float netData[160U]; // Modem can handle up to 160 samples at a time unsigned int length = m_network->read(netData, 160U); //160 samples 12-bit @@ -146,14 +156,17 @@ unsigned int CFMControl::readModem(unsigned char* data, unsigned int space) unsigned int nData = 0U; for (unsigned int i = 0; i < length; i++) { + float sampleFloat = netData[i] * m_txAudioGain; + // Pre-emphasis - float sampleFloat = m_preemphasis->filter(netData[i]); + if (m_preEmphasisOn) + sampleFloat = m_preEmphasis->filter(sampleFloat); // Convert float to 12-bit samples (0 to 4095) unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F); - // pack 2 samples onto 3 bytes - if((i & 1U) == 0) { + // Pack 2 samples into 3 bytes + if ((i & 1U) == 0) { pack = 0U; pack = sample12bit << 12; } else { diff --git a/FMControl.h b/FMControl.h index ac2142ec1..9344f773b 100644 --- a/FMControl.h +++ b/FMControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX * * 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 @@ -30,7 +30,7 @@ class CFMControl { public: - CFMControl(CFMNetwork* network); + CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn); ~CFMControl(); bool writeModem(const unsigned char* data, unsigned int length); @@ -43,10 +43,14 @@ class CFMControl { private: CFMNetwork* m_network; + float m_txAudioGain; + float m_rxAudioGain; + bool m_preEmphasisOn; + bool m_deEmphasisOn; bool m_enabled; CRingBuffer m_incomingRFAudio; - CIIRDirectForm1Filter* m_preemphasis; - CIIRDirectForm1Filter* m_deemphasis; + CIIRDirectForm1Filter* m_preEmphasis; + CIIRDirectForm1Filter* m_deEmphasis; CIIRDirectForm1Filter* m_filterStage1; CIIRDirectForm1Filter* m_filterStage2; CIIRDirectForm1Filter* m_filterStage3; diff --git a/FMNetwork.cpp b/FMNetwork.cpp index d9aa0e511..85947c91a 100644 --- a/FMNetwork.cpp +++ b/FMNetwork.cpp @@ -27,8 +27,8 @@ const unsigned int BUFFER_LENGTH = 500U; -CFMNetwork::CFMNetwork(const std::string& format, const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug) : -m_format(FMF_MMDVM), +CFMNetwork::CFMNetwork(const std::string& protocol, const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug) : +m_protocol(FMNP_MMDVM), m_socket(localAddress, localPort), m_addr(), m_addrLen(0U), @@ -46,8 +46,8 @@ m_seqNo(0U) if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) m_addrLen = 0U; - if (format == "USRP") - m_format = FMF_USRP; + if (protocol == "USRP") + m_protocol = FMNP_USRP; #if !defined(_WIN32) && !defined(_WIN64) int error; @@ -116,7 +116,7 @@ bool CFMNetwork::writeData(float* data, unsigned int nSamples) unsigned int length = 0U; - if (m_format == FMF_USRP) { + if (m_protocol == FMNP_USRP) { buffer[length++] = 'U'; buffer[length++] = 'S'; buffer[length++] = 'R'; @@ -187,7 +187,7 @@ bool CFMNetwork::writeData(float* data, unsigned int nSamples) bool CFMNetwork::writeEOT() { - if (m_format == FMF_MMDVM) { + if (m_protocol == FMNP_MMDVM) { unsigned char buffer[10U]; ::memset(buffer, 0x00U, 10U); @@ -220,11 +220,11 @@ void CFMNetwork::clock(unsigned int ms) if (length <= 0) return; - // Check if the data is for us // does not accept data from USRP - //if (!CUDPSocket::match(addr, m_addr)) { - // LogMessage("FM packet received from an invalid source"); - // return; - //} + // Check if the data is for us + if (!CUDPSocket::match(addr, m_addr)) { + LogMessage("FM packet received from an invalid source"); + return; + } if (!m_enabled) return; @@ -232,7 +232,7 @@ void CFMNetwork::clock(unsigned int ms) if (m_debug) CUtils::dump(1U, "FM Network Data Received", buffer, length); - if (m_format == FMF_USRP) { + if (m_protocol == FMNP_USRP) { // Invalid packet type? if (::memcmp(buffer, "USRP", 4U) != 0) return; @@ -304,9 +304,9 @@ unsigned int CFMNetwork::read(float* data, unsigned int nSamples) } else { #endif for (unsigned int i = 0U; i < nSamples; i++) { - short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + // Changing audio format from U16BE to S16LE - ((buffer[i * 2U + 1U] & 0xFFU) << 8); // Changing audio format from U16BE to S16LE - data[i] = (float(val) / 65536.0F); // Changing audio format from U16BE to S16LE + short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + + ((buffer[i * 2U + 1U] & 0xFFU) << 8); + data[i] = float(val) / 65536.0F; } return nSamples; @@ -347,7 +347,7 @@ void CFMNetwork::enable(bool enabled) void CFMNetwork::writePoll() { - if (m_format == FMF_MMDVM) { + if (m_protocol == FMNP_MMDVM) { unsigned char buffer[3U]; buffer[0U] = 'F'; diff --git a/FMNetwork.h b/FMNetwork.h index d7b1ff74d..90f4225e5 100644 --- a/FMNetwork.h +++ b/FMNetwork.h @@ -30,14 +30,14 @@ #include #include -enum FM_FORMAT { - FMF_MMDVM, - FMF_USRP +enum FM_NETWORK_PROTOCOL { + FMNP_MMDVM, + FMNP_USRP }; class CFMNetwork { public: - CFMNetwork(const std::string& format, const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug); + CFMNetwork(const std::string& protocol, const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug); ~CFMNetwork(); bool open(); @@ -57,19 +57,19 @@ class CFMNetwork { void clock(unsigned int ms); private: - FM_FORMAT m_format; - CUDPSocket m_socket; - sockaddr_storage m_addr; - unsigned int m_addrLen; - unsigned int m_sampleRate; - bool m_debug; - bool m_enabled; + FM_NETWORK_PROTOCOL m_protocol; + CUDPSocket m_socket; + sockaddr_storage m_addr; + unsigned int m_addrLen; + unsigned int m_sampleRate; + bool m_debug; + bool m_enabled; CRingBuffer m_buffer; - CTimer m_pollTimer; - unsigned int m_seqNo; + CTimer m_pollTimer; + unsigned int m_seqNo; #if !defined(_WIN32) && !defined(_WIN64) - SRC_STATE* m_incoming; - SRC_STATE* m_outgoing; + SRC_STATE* m_incoming; + SRC_STATE* m_outgoing; #endif void writePoll(); diff --git a/MMDVM.ini b/MMDVM.ini index 255c12d8c..be1dbcf9d 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -276,12 +276,16 @@ Debug=0 [FM Network] Enable=1 # Values are MMDVM and USRP -Format=USRP +Protocol=USRP LocalAddress=127.0.0.1 LocalPort=3810 GatewayAddress=127.0.0.1 GatewayPort=4810 SampleRate=8000 +PreEmphasis=1 +DeEmphasis=1 +TXAudioGain=1.0 +RXAudioGain=1.0 # ModeHang=3 Debug=0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index b78d7e9e8..2f51e69d1 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -681,15 +681,19 @@ int CMMDVMHost::run() } if (m_fmEnabled) { - m_fmRFModeHang = m_conf.getFMModeHang(); + bool preEmphasis = m_conf.getFMPreEmphasis(); + bool deEmphasis = m_conf.getFMDeEmphasis(); + float txAudioGain = m_conf.getFMTXAudioGain(); + float rxAudioGain = m_conf.getFMRXAudioGain(); + m_fmRFModeHang = m_conf.getFMModeHang(); - m_fm = new CFMControl(m_fmNetwork); + m_fm = new CFMControl(m_fmNetwork, txAudioGain, rxAudioGain, preEmphasis, deEmphasis); } bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { std::string address = m_conf.getRemoteControlAddress(); - unsigned int port = m_conf.getRemoteControlPort(); + unsigned int port = m_conf.getRemoteControlPort(); LogInfo("Remote Control Parameters"); LogInfo(" Address: %s", address.c_str()); @@ -1796,25 +1800,33 @@ bool CMMDVMHost::createPOCSAGNetwork() bool CMMDVMHost::createFMNetwork() { - std::string format = m_conf.getFMNetworkFormat(); + std::string protocol = m_conf.getFMNetworkProtocol(); std::string gatewayAddress = m_conf.getFMGatewayAddress(); unsigned int gatewayPort = m_conf.getFMGatewayPort(); std::string localAddress = m_conf.getFMLocalAddress(); unsigned int localPort = m_conf.getFMLocalPort(); unsigned int sampleRate = m_conf.getFMSampleRate(); + bool preEmphasis = m_conf.getFMPreEmphasis(); + bool deEmphasis = m_conf.getFMDeEmphasis(); + float txAudioGain = m_conf.getFMTXAudioGain(); + float rxAudioGain = m_conf.getFMRXAudioGain(); m_fmNetModeHang = m_conf.getFMNetworkModeHang(); bool debug = m_conf.getFMNetworkDebug(); LogInfo("FM Network Parameters"); - LogInfo(" Format: %s", format.c_str()); + LogInfo(" Protocol: %s", protocol.c_str()); LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); LogInfo(" Gateway Port: %u", gatewayPort); LogInfo(" Local Address: %s", localAddress.c_str()); LogInfo(" Local Port: %u", localPort); LogInfo(" Sample Rate: %u", sampleRate); + LogInfo(" Pre-Emphasis: %s", preEmphasis ? "yes" : "no"); + LogInfo(" De-Emphasis: %s", deEmphasis ? "yes" : "no"); + LogInfo(" TX Audio Gain: %.2f", txAudioGain); + LogInfo(" RX Audio Gain: %.2f", rxAudioGain); LogInfo(" Mode Hang: %us", m_fmNetModeHang); - m_fmNetwork = new CFMNetwork(format, localAddress, localPort, gatewayAddress, gatewayPort, sampleRate, debug); + m_fmNetwork = new CFMNetwork(protocol, localAddress, localPort, gatewayAddress, gatewayPort, sampleRate, debug); bool ret = m_fmNetwork->open(); if (!ret) {