diff --git a/CalFM.cpp b/CalFM.cpp new file mode 100644 index 0000000..4215055 --- /dev/null +++ b/CalFM.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * Copyright (C) 2016 by Colin Durbridge G4EML + * Copyright (C) 2020 by Phil Taylor M0VSE + * + * 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 "CalFM.h" + + +const struct TONE_TABLE { + uint16_t frequency; + uint16_t length; + q31_t increment; +} TONE_TABLE_DATA[] = { + {2495U, 10U, 223248821}, + {2079U, 12U, 186025771}, + {1633U, 15U, 148802721}, + {1247U, 20U, 111579672}, + {1039U, 24U, 93012886}, + {956U, 26U, 85541432}}; + + +const uint8_t TONE_TABLE_DATA_LEN = 6U; + +CCalFM::CCalFM() : +m_tone(NULL), +m_frequency(0), +m_length(0), +m_level(128*128), +m_transmit(false), +m_lastState(STATE_IDLE), +m_audioSeq(0) +{ +} + +void CCalFM::process() +{ + const TONE_TABLE* entry = NULL; + + if (m_modemState!=m_lastState) + { + switch (m_modemState) { + case STATE_FMCAL10K: + m_frequency=956U; + break; + case STATE_FMCAL12K: + m_frequency=1039U; + break; + case STATE_FMCAL15K: + m_frequency=1247U; + break; + case STATE_FMCAL20K: + m_frequency=1633U; + break; + case STATE_FMCAL25K: + m_frequency=2079U; + break; + case STATE_FMCAL30K: + m_frequency=2495U; + break; + default: + m_frequency=0; + break; + } + + for (uint8_t i = 0U; i < TONE_TABLE_DATA_LEN; i++) { + if (TONE_TABLE_DATA[i].frequency == m_frequency) { + entry = TONE_TABLE_DATA + i; + break; + } + } + + if (entry == NULL) + return; + + m_length = entry->length; + + delete[] m_tone; + m_tone = new q15_t[m_length]; + + q31_t arg = 0; + for (uint16_t i = 0U; i < m_length; i++) { + q63_t value = ::arm_sin_q31(arg) * q63_t(m_level * 13); + m_tone[i] = q15_t(__SSAT((value >> 31), 16)); + + arg += entry->increment; + } + + m_lastState=m_modemState; + } + + if (m_transmit) + { + uint16_t space = io.getSpace(); + while (space > m_length) + { + io.write(m_modemState,m_tone,m_length); + space -= m_length; + } + } +} + + +uint8_t CCalFM::write(const uint8_t* data, uint8_t length) +{ + if (length != 1U) + return 4U; + + m_transmit = data[0U] == 1U; + + return 0U; +} + diff --git a/CalFM.h b/CalFM.h new file mode 100644 index 0000000..0dd8887 --- /dev/null +++ b/CalFM.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX + * Copyright (C) 2016 by Colin Durbridge G4EML + * Copyright (C) 2020 by Phil Taylor M0VSE + * + * 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(CALFM_H) +#define CALFM_H + +#include "Config.h" + +class CCalFM { +public: + CCalFM(); + + void process(); + void fm10kcal(); + void fm12k5cal(); + void fm15kcal(); + void fm20kcal(); + void fm25kcal(); + void fm30kcal(); + + uint8_t write(const uint8_t* data, uint8_t length); + +private: + uint16_t m_frequency; + uint16_t m_length; + q15_t* m_tone; + q15_t m_level; + bool m_transmit; + uint8_t m_audioSeq; + uint8_t m_lastState; +}; + +#endif + diff --git a/Globals.h b/Globals.h index 75d37e0..5cb147a 100644 --- a/Globals.h +++ b/Globals.h @@ -63,7 +63,13 @@ enum MMDVM_STATE { STATE_DMRCAL = 98, STATE_DSTARCAL = 99, STATE_INTCAL = 100, - STATE_POCSAGCAL = 101 + STATE_POCSAGCAL = 101, + STATE_FMCAL10K = 102, + STATE_FMCAL12K = 103, + STATE_FMCAL15K = 104, + STATE_FMCAL20K = 105, + STATE_FMCAL25K = 106, + STATE_FMCAL30K = 107 }; #include "SerialPort.h" @@ -83,6 +89,7 @@ enum MMDVM_STATE { #include "POCSAGTX.h" #include "CalDStarRX.h" #include "CalDStarTX.h" +#include "CalFM.h" #include "CalDMR.h" #include "CalP25.h" #include "CalNXDN.h" @@ -152,6 +159,7 @@ extern CFM fm; extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; +extern CCalFM calFM; extern CCalP25 calP25; extern CCalNXDN calNXDN; extern CCalPOCSAG calPOCSAG; diff --git a/MMDVM.cpp b/MMDVM.cpp index f0e095a..a6d4577 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -67,6 +67,7 @@ CCalDStarTX calDStarTX; CCalDMR calDMR; CCalP25 calP25; CCalNXDN calNXDN; +CCalFM calFM; CCalPOCSAG calPOCSAG; CCalRSSI calRSSI; @@ -127,6 +128,9 @@ void loop() if (m_modemState == STATE_POCSAGCAL) calPOCSAG.process(); + if (m_modemState == STATE_FMCAL10K || m_modemState == STATE_FMCAL12K || m_modemState == STATE_FMCAL15K || m_modemState == STATE_FMCAL20K || m_modemState == STATE_FMCAL25K ||m_modemState == STATE_FMCAL30K) + calFM.process(); + if (m_modemState == STATE_IDLE) cwIdTX.process(); } diff --git a/MMDVM.ino b/MMDVM.ino index f7b2182..17c4ad8 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -62,6 +62,7 @@ CFM fm; CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; +CCalFM calFM; CCalP25 calP25; CCalNXDN calNXDN; CCalPOCSAG calPOCSAG; @@ -115,6 +116,9 @@ void loop() if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) calDMR.process(); + if (m_modemState == STATE_FMCAL10K || m_modemState == STATE_FMCAL12K || m_modemState == STATE_FMCAL15K || m_modemState == STATE_FMCAL20K || m_modemState == STATE_FMCAL25K || m_modemState == STATE_FMCAL30K) + calFM.process(); + if (m_modemState == STATE_P25CAL1K) calP25.process(); diff --git a/MMDVM_STM32F4xx.coproj b/MMDVM_STM32F4xx.coproj index 2990342..4f1592a 100644 --- a/MMDVM_STM32F4xx.coproj +++ b/MMDVM_STM32F4xx.coproj @@ -273,6 +273,7 @@ + @@ -290,6 +291,7 @@ + @@ -335,4 +337,4 @@ - \ No newline at end of file + diff --git a/SerialPort.cpp b/SerialPort.cpp index da81dfd..84282d8 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -287,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_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) + 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 && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -440,7 +440,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_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) + 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 && modemState != STATE_FMCAL10K && modemState != STATE_FMCAL12K && modemState != STATE_FMCAL15K && modemState != STATE_FMCAL20K && modemState != STATE_FMCAL25K && modemState != STATE_FMCAL30K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -498,8 +498,23 @@ void CSerialPort::setMode(MMDVM_STATE modemState) case STATE_LFCAL: DEBUG1("Mode set to 80 Hz Calibrate"); break; - case STATE_DMRCAL1K: - DEBUG1("Mode set to DMR BS 1031 Hz Calibrate"); + case STATE_FMCAL10K: + DEBUG1("Mode set to FM 10Khz Calibrate"); + break; + case STATE_FMCAL12K: + DEBUG1("Mode set to FM 12.5Khz Calibrate"); + break; + case STATE_FMCAL15K: + DEBUG1("Mode set to FM 15Khz Calibrate"); + break; + case STATE_FMCAL20K: + DEBUG1("Mode set to FM 20Khz Calibrate"); + break; + case STATE_FMCAL25K: + DEBUG1("Mode set to FM 10Khz Calibrate"); + break; + case STATE_FMCAL30K: + DEBUG1("Mode set to FM 30Khz Calibrate"); break; case STATE_P25CAL1K: DEBUG1("Mode set to P25 1011 Hz Calibrate"); @@ -648,6 +663,8 @@ void CSerialPort::process() err = calDStarTX.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) err = calDMR.write(m_buffer + 3U, m_len - 3U); + if (m_modemState == STATE_FMCAL10K || m_modemState == STATE_FMCAL12K || m_modemState == STATE_FMCAL15K || m_modemState == STATE_FMCAL20K || m_modemState == STATE_FMCAL25K || m_modemState == STATE_FMCAL30K) + err = calFM.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_P25CAL1K) err = calP25.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_NXDNCAL1K)