diff --git a/CalFM.cpp b/CalFM.cpp
new file mode 100644
index 0000000..4a0b062
--- /dev/null
+++ b/CalFM.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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, 140271371},
+ {2079U, 12U, 116883439},
+ {1633U, 15U, 91808877},
+ {1247U, 20U, 70107575},
+ {1039U, 24U, 58441775},
+ {956U, 26U, 53747266}};
+*/
+
+
+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 f729cb3..594c7ef 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;
@@ -439,7 +439,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;
@@ -497,8 +497,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");
@@ -647,6 +662,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)