From 9d725b94799f64139de665818f8d4111498b9d82 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 11 Apr 2020 22:08:17 +0100 Subject: [PATCH] Beginnings of FM controller support. --- FM.cpp | 37 ++++++++++++++++ FM.h | 37 ++++++++++++++++ Globals.h | 8 +++- IO.cpp | 26 +++++++++-- IO.h | 4 +- MMDVM.cpp | 8 +++- MMDVM.ino | 9 +++- SerialPort.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++-- SerialPort.h | 4 +- 9 files changed, 234 insertions(+), 15 deletions(-) create mode 100644 FM.cpp create mode 100644 FM.h diff --git a/FM.cpp b/FM.cpp new file mode 100644 index 0000000..ee219ae --- /dev/null +++ b/FM.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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 + * the Free Software Foundation; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "FM.h" + +CFM::CFM() +{ +} + +void CFM::samples(bool cos, const q15_t* samples, uint8_t length) +{ +} + +void CFM::process() +{ +} + +void CFM::reset() +{ +} diff --git a/FM.h b/FM.h new file mode 100644 index 0000000..ca62586 --- /dev/null +++ b/FM.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 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 + * the Free Software Foundation; either version 2 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(FM_H) +#define FM_H + +#include "Config.h" + +class CFM { +public: + CFM(); + + void samples(bool cos, const q15_t* samples, uint8_t length); + + void process(); + + void reset(); + +private: +}; + +#endif diff --git a/Globals.h b/Globals.h index 149cb25..75d37e0 100644 --- a/Globals.h +++ b/Globals.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020 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 @@ -50,6 +50,7 @@ enum MMDVM_STATE { STATE_P25 = 4, STATE_NXDN = 5, STATE_POCSAG = 6, + STATE_FM = 10, // Dummy states start at 90 STATE_NXDNCAL1K = 91, @@ -90,6 +91,7 @@ enum MMDVM_STATE { #include "CWIdTX.h" #include "Debug.h" #include "IO.h" +#include "FM.h" const uint8_t MARK_SLOT1 = 0x08U; const uint8_t MARK_SLOT2 = 0x04U; @@ -114,6 +116,7 @@ extern bool m_ysfEnable; extern bool m_p25Enable; extern bool m_nxdnEnable; extern bool m_pocsagEnable; +extern bool m_fmEnable; extern bool m_duplex; @@ -144,6 +147,8 @@ extern CNXDNTX nxdnTX; extern CPOCSAGTX pocsagTX; +extern CFM fm; + extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; @@ -155,4 +160,3 @@ extern CCalRSSI calRSSI; extern CCWIdTX cwIdTX; #endif - diff --git a/IO.cpp b/IO.cpp index 8267070..8f3e54e 100644 --- a/IO.cpp +++ b/IO.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -402,6 +402,15 @@ void CIO::process() dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE); } } + + if (m_fmEnable) { + bool cos = getCOSInt(); +#if defined(USE_DCBLOCKER) + fm.samples(cos, dcSamples, RX_BLOCK_SIZE); +#else + fm.samples(cos, samples, RX_BLOCK_SIZE); +#endif + } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { q15_t GMSKVals[RX_BLOCK_SIZE]; @@ -460,6 +469,13 @@ void CIO::process() nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } + } else if (m_modemState == STATE_FM) { + bool cos = getCOSInt(); +#if defined(USE_DCBLOCKER) + fm.samples(cos, dcSamples, RX_BLOCK_SIZE); +#else + fm.samples(cos, samples, RX_BLOCK_SIZE); +#endif } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); @@ -505,6 +521,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t case STATE_POCSAG: txLevel = m_pocsagTXLevel; break; + case STATE_FM: + txLevel = m_fmTXLevel; + break; default: txLevel = m_cwIdTXLevel; break; @@ -553,10 +572,11 @@ void CIO::setMode() setP25Int(m_modemState == STATE_P25); setNXDNInt(m_modemState == STATE_NXDN); setPOCSAGInt(m_modemState == STATE_POCSAG); + setFMInt(m_modemState == STATE_FM); #endif } -void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset) +void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset) { m_pttInvert = pttInvert; @@ -568,6 +588,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_p25TXLevel = q15_t(p25TXLevel * 128); m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); m_pocsagTXLevel = q15_t(pocsagTXLevel * 128); + m_fmTXLevel = q15_t(fmTXLevel * 128); m_rxDCOffset = DC_OFFSET + rxDCOffset; m_txDCOffset = DC_OFFSET + txDCOffset; @@ -618,4 +639,3 @@ bool CIO::hasLockout() const { return m_lockout; } - diff --git a/IO.h b/IO.h index 4a7df28..21eccc0 100644 --- a/IO.h +++ b/IO.h @@ -42,7 +42,7 @@ public: void interrupt(); - void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, int16_t txDCOffset, int16_t rxDCOffset); + void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, uint8_t pocsagTXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset); void getOverflow(bool& adcOverflow, bool& dacOverflow); @@ -86,6 +86,7 @@ private: q15_t m_p25TXLevel; q15_t m_nxdnTXLevel; q15_t m_pocsagTXLevel; + q15_t m_fmTXLevel; uint16_t m_rxDCOffset; uint16_t m_txDCOffset; @@ -123,4 +124,3 @@ private: }; #endif - diff --git a/MMDVM.cpp b/MMDVM.cpp index 306cdcd..f0e095a 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Mathis Schmieder DB9MAT * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -32,6 +32,7 @@ bool m_ysfEnable = true; bool m_p25Enable = true; bool m_nxdnEnable = true; bool m_pocsagEnable = true; +bool m_fmEnable = true; bool m_duplex = true; @@ -59,6 +60,8 @@ CNXDNTX nxdnTX; CPOCSAGTX pocsagTX; +CFM fm; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -106,6 +109,9 @@ void loop() if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy())) pocsagTX.process(); + if (m_fmEnable && m_modemState == STATE_FM) + fm.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); diff --git a/MMDVM.ino b/MMDVM.ino index dcb1fcf..f7b2182 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * * This program is free software; you can redistribute it and/or modify @@ -29,6 +29,7 @@ bool m_ysfEnable = true; bool m_p25Enable = true; bool m_nxdnEnable = true; bool m_pocsagEnable = true; +bool m_fmEnable = true; bool m_duplex = true; @@ -56,6 +57,8 @@ CNXDNTX nxdnTX; CPOCSAGTX pocsagTX; +CFM fm; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -103,6 +106,9 @@ void loop() if (m_pocsagEnable && (m_modemState == STATE_POCSAG || pocsagTX.busy())) pocsagTX.process(); + if (m_fmEnable && m_modemState == STATE_FM) + fm.process(); + if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); @@ -121,4 +127,3 @@ void loop() if (m_modemState == STATE_IDLE) cwIdTX.process(); } - diff --git a/SerialPort.cpp b/SerialPort.cpp index 11d57e5..5a737fc 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013,2015-2019 by Jonathan Naylor G4KLX + * Copyright (C) 2013,2015-2020 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * * This program is free software; you can redistribute it and/or modify @@ -65,6 +65,10 @@ const uint8_t MMDVM_NXDN_LOST = 0x41U; const uint8_t MMDVM_POCSAG_DATA = 0x50U; +const uint8_t MMDVM_FM_PARAMS1 = 0x60U; +const uint8_t MMDVM_FM_PARAMS2 = 0x61U; +const uint8_t MMDVM_FM_PARAMS3 = 0x62U; + const uint8_t MMDVM_ACK = 0x70U; const uint8_t MMDVM_NAK = 0x7FU; @@ -97,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20190130 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG)" +#define DESCRIPTION "20200411 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -168,6 +172,8 @@ void CSerialPort::getStatus() reply[3U] |= 0x10U; if (m_pocsagEnable) reply[3U] |= 0x20U; + if (m_fmEnable) + reply[3U] |= 0x40U; reply[4U] = uint8_t(m_modemState); @@ -273,6 +279,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) bool p25Enable = (data[1U] & 0x08U) == 0x08U; bool nxdnEnable = (data[1U] & 0x10U) == 0x10U; bool pocsagEnable = (data[1U] & 0x20U) == 0x20U; + bool fmEnable = (data[1U] & 0x40U) == 0x40U; uint8_t txDelay = data[2U]; if (txDelay > 50U) @@ -280,7 +287,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) MMDVM_STATE modemState = MMDVM_STATE(data[3U]); - if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -294,6 +301,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_POCSAG && !pocsagEnable) return 4U; + if (modemState == STATE_FM && !fmEnable) + return 4U; uint8_t rxLevel = data[4U]; @@ -326,6 +335,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) m_p25Enable = p25Enable; m_nxdnEnable = nxdnEnable; m_pocsagEnable = pocsagEnable; + m_fmEnable = fmEnable; m_duplex = !simplex; dstarTX.setTXDelay(txDelay); @@ -350,6 +360,69 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) return 0U; } +uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) +{ + if (length < 8U) + return 4U; + + uint8_t speed = data[0U];; + uint16_t frequency = data[1U] * 10U; + uint8_t time = data[2U]; + uint8_t holdoff = data[3U]; + uint8_t highLevel = data[4U]; + uint8_t lowLevel = data[5U]; + + bool callAtStart = (data[6U] & 0x01U) == 0x01U; + bool callAtEnd = (data[6U] & 0x02U) == 0x02U; + + char callsign; + uint8_t n = 0U; + for (uint8_t i = 7U; i < length; i++, n++) + callsign[n] = data[i]; + callsign[n] = '\0'; + + return 0U; +} + +uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) +{ + if (length < 4U) + return 4U; + + uint8_t speed = data[0U]; + uint16_t frequency = data[1U] * 10U; + uint8_t delay = data[2U] * 10U; + uint8_t level = data[3U]; + + char ack; + uint8_t n = 0U; + for (uint8_t i = 4U; i < length; i++, n++) + ack[n] = data[i]; + ack[n] = '\0'; + + return 0U; +} + +uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) +{ + if (length < 9U) + return 4U; + + uint16_t timeout = data[0U] * 5U; + uint8_t timeoutLevel = data[1U]; + + uint8_t ctcssFrequency = data[2U]; + uint8_t ctcssThreshold = data[3U]; + uint8_t ctcssLevel = data[4U]; + + uint8_t inputLevel = data[5U]; + uint8_t outputLevel = data[6U]; + uint8_t kerchunkTime = data[7U]; + uint8_t hangTime = data[8U]; + + return 0U; +} + uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) { if (length < 1U) @@ -360,7 +433,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) if (modemState == m_modemState) return 0U; - if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_POCSAG && modemState != STATE_FM && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K && modemState != STATE_POCSAGCAL) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -374,6 +447,8 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) return 4U; if (modemState == STATE_POCSAG && !m_pocsagEnable) return 4U; + if (modemState == STATE_FM && !m_fmEnable) + return 4U; setMode(modemState); @@ -401,6 +476,9 @@ void CSerialPort::setMode(MMDVM_STATE modemState) case STATE_POCSAG: DEBUG1("Mode set to POCSAG"); break; + case STATE_FM: + DEBUG1("Mode set to FM"); + break; case STATE_DSTARCAL: DEBUG1("Mode set to D-Star Calibrate"); break; @@ -525,6 +603,36 @@ void CSerialPort::process() sendACK(); break; + case MMDVM_FM_PARAMS1: + err = setFMParams1(m_buffer + 3U, m_len - 3U); + if (err == 0U) { + sendACK(); + } else { + DEBUG2("Received invalid FM params 1", err); + sendNAK(err); + } + break; + + case MMDVM_FM_PARAMS2: + err = setFMParams2(m_buffer + 3U, m_len - 3U); + if (err == 0U) { + sendACK(); + } else { + DEBUG2("Received invalid FM params 2", err); + sendNAK(err); + } + break; + + case MMDVM_FM_PARAMS3: + err = setFMParams3(m_buffer + 3U, m_len - 3U); + if (err == 0U) { + sendACK(); + } else { + DEBUG2("Received invalid FM params 3", err); + sendNAK(err); + } + break; + case MMDVM_CAL_DATA: if (m_modemState == STATE_DSTARCAL) err = calDStarTX.write(m_buffer + 3U, m_len - 3U); diff --git a/SerialPort.h b/SerialPort.h index 728b6b3..cd09bbe 100644 --- a/SerialPort.h +++ b/SerialPort.h @@ -73,6 +73,9 @@ private: uint8_t setConfig(const uint8_t* data, uint8_t length); uint8_t setMode(const uint8_t* data, uint8_t length); void setMode(MMDVM_STATE modemState); + uint8_t setFMParams1(const uint8_t* data, uint8_t length); + uint8_t setFMParams2(const uint8_t* data, uint8_t length); + uint8_t setFMParams3(const uint8_t* data, uint8_t length); // Hardware versions void beginInt(uint8_t n, int speed); @@ -83,4 +86,3 @@ private: }; #endif -