From 9d725b94799f64139de665818f8d4111498b9d82 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 11 Apr 2020 22:08:17 +0100 Subject: [PATCH 001/119] 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 - From ef65268b45df861d0484f0f2cda8aefc1d1c6649 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 12 Apr 2020 15:28:56 +0100 Subject: [PATCH 002/119] First version of initialisation. --- FM.cpp | 12 ++++++ FM.h | 4 ++ IO.cpp | 113 ++++++++++++++++--------------------------------- IO.h | 3 +- SerialPort.cpp | 30 ++++++++----- 5 files changed, 75 insertions(+), 87 deletions(-) diff --git a/FM.cpp b/FM.cpp index ee219ae..466a281 100644 --- a/FM.cpp +++ b/FM.cpp @@ -35,3 +35,15 @@ void CFM::process() void CFM::reset() { } + +void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd) +{ +} + +void CFM::setAck(const char* ack, uint8_t speed, uint16_t frequency, uint16_t delay, uint8_t level) +{ +} + +void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) +{ +} diff --git a/FM.h b/FM.h index ca62586..a315869 100644 --- a/FM.h +++ b/FM.h @@ -31,6 +31,10 @@ public: void reset(); + void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd); + void setAck(const char* ack, uint8_t speed, uint16_t frequency, uint16_t delay, uint8_t level); + void setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); + private: }; diff --git a/IO.cpp b/IO.cpp index 8f3e54e..94b0cbb 100644 --- a/IO.cpp +++ b/IO.cpp @@ -80,6 +80,8 @@ m_ysfTXLevel(128 * 128), m_p25TXLevel(128 * 128), m_nxdnTXLevel(128 * 128), m_pocsagTXLevel(128 * 128), +m_fmRXLevel(128 * 128), +m_fmTXLevel(128 * 128), m_rxDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET), m_ledCount(0U), @@ -144,6 +146,7 @@ void CIO::selfTest() setP25Int(ledValue); setNXDNInt(ledValue); setPOCSAGInt(ledValue); + setFMInt(ledValue); #endif delayInt(250); } @@ -155,105 +158,46 @@ void CIO::selfTest() setP25Int(false); setNXDNInt(false); setPOCSAGInt(false); + setFMInt(false); delayInt(250); - - setDStarInt(true); setDMRInt(true); - setYSFInt(false); - setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); delayInt(250); - - setDStarInt(true); - setDMRInt(true); setYSFInt(true); - setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); - - delayInt(250); - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); + delayInt(250); setP25Int(true); - setNXDNInt(false); - setPOCSAGInt(false); - - delayInt(250); - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); - setP25Int(true); + delayInt(250); setNXDNInt(true); - setPOCSAGInt(false); - - delayInt(250); - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); - setP25Int(true); - setNXDNInt(true); + delayInt(250); setPOCSAGInt(true); - - delayInt(250); - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); - setP25Int(true); - setNXDNInt(true); + delayInt(250); + setFMInt(true); + + delayInt(250); + setFMInt(false); + + delayInt(250); setPOCSAGInt(false); - - delayInt(250); - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); - setP25Int(true); + delayInt(250); setNXDNInt(false); - setPOCSAGInt(false); delayInt(250); - - setDStarInt(true); - setDMRInt(true); - setYSFInt(true); setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); delayInt(250); - - setDStarInt(true); - setDMRInt(true); setYSFInt(false); - setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); delayInt(250); - - setDStarInt(true); setDMRInt(false); - setYSFInt(false); - setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); delayInt(250); - setDStarInt(false); - setDMRInt(false); - setYSFInt(false); - setP25Int(false); - setNXDNInt(false); - setPOCSAGInt(false); #endif } @@ -405,11 +349,19 @@ void CIO::process() if (m_fmEnable) { bool cos = getCOSInt(); + q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) - fm.samples(cos, dcSamples, RX_BLOCK_SIZE); + for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { + q31_t res1 = dcSamples[i] * m_fmRXLevel; + FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); + } #else - fm.samples(cos, samples, RX_BLOCK_SIZE); + for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { + q31_t res1 = samples[i] * m_fmRXLevel; + FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); + } #endif + fm.samples(cos, FMVals, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -471,11 +423,19 @@ void CIO::process() } } else if (m_modemState == STATE_FM) { bool cos = getCOSInt(); + q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) - fm.samples(cos, dcSamples, RX_BLOCK_SIZE); + for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { + q31_t res1 = dcSamples[i] * m_fmRXLevel; + FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); + } #else - fm.samples(cos, samples, RX_BLOCK_SIZE); + for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { + q31_t res1 = samples[i] * m_fmRXLevel; + FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); + } #endif + fm.samples(cos, FMVals, RX_BLOCK_SIZE); } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); @@ -576,7 +536,7 @@ void CIO::setMode() #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, uint8_t fmTXLevel, 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 fmRXLevel, uint8_t fmTXLevel, int16_t txDCOffset, int16_t rxDCOffset) { m_pttInvert = pttInvert; @@ -588,6 +548,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_fmRXLevel = q15_t(fmRXLevel * 128); m_fmTXLevel = q15_t(fmTXLevel * 128); m_rxDCOffset = DC_OFFSET + rxDCOffset; diff --git a/IO.h b/IO.h index 21eccc0..1fea082 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, uint8_t fmTXLevel, 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 fmRXLevel, 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_fmRXLevel; q15_t m_fmTXLevel; uint16_t m_rxDCOffset; diff --git a/SerialPort.cpp b/SerialPort.cpp index 5a737fc..872657a 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -262,7 +262,7 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 18U) + if (length < 20U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; @@ -327,6 +327,9 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t pocsagTXLevel = data[17U]; + uint8_t fmTXLevel = data[18U]; + uint8_t fmRXLevel = data[19U]; + m_modemState = modemState; m_dstarEnable = dstarEnable; @@ -353,7 +356,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) ysfTX.setParams(ysfLoDev, ysfTXHang); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, txDCOffset, rxDCOffset); + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, fmRXLevel, txDCOffset, rxDCOffset); io.start(); @@ -375,12 +378,14 @@ uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) bool callAtStart = (data[6U] & 0x01U) == 0x01U; bool callAtEnd = (data[6U] & 0x02U) == 0x02U; - char callsign; + char callsign[50U]; uint8_t n = 0U; for (uint8_t i = 7U; i < length; i++, n++) callsign[n] = data[i]; callsign[n] = '\0'; + fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd); + return 0U; } @@ -391,21 +396,23 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t speed = data[0U]; uint16_t frequency = data[1U] * 10U; - uint8_t delay = data[2U] * 10U; + uint16_t delay = data[2U] * 10U; uint8_t level = data[3U]; - char ack; + char ack[50U]; uint8_t n = 0U; for (uint8_t i = 4U; i < length; i++, n++) ack[n] = data[i]; ack[n] = '\0'; + fm.setAck(ack, speed, frequency, delay, level); + return 0U; } uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 9U) + if (length < 7U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -415,10 +422,10 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) 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]; + uint8_t kerchunkTime = data[5U]; + uint8_t hangTime = data[6U]; + + fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); return 0U; } @@ -529,6 +536,9 @@ void CSerialPort::setMode(MMDVM_STATE modemState) if (modemState != STATE_NXDN) nxdnRX.reset(); + if (modemState != STATE_FM) + fm.reset(); + cwIdTX.reset(); m_modemState = modemState; From c84d81d91c928e9c147ee2cf839ebca9ce6255e8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 13 Apr 2020 15:54:37 +0100 Subject: [PATCH 003/119] Add the minimum time for a 'K' parameter. --- FM.cpp | 2 +- FM.h | 2 +- SerialPort.cpp | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/FM.cpp b/FM.cpp index 466a281..f500ccb 100644 --- a/FM.cpp +++ b/FM.cpp @@ -40,7 +40,7 @@ void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, u { } -void CFM::setAck(const char* ack, uint8_t speed, uint16_t frequency, uint16_t delay, uint8_t level) +void CFM::setAck(const char* ack, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) { } diff --git a/FM.h b/FM.h index a315869..cbbd72b 100644 --- a/FM.h +++ b/FM.h @@ -32,7 +32,7 @@ public: void reset(); void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd); - void setAck(const char* ack, uint8_t speed, uint16_t frequency, uint16_t delay, uint8_t level); + void setAck(const char* ack, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); void setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: diff --git a/SerialPort.cpp b/SerialPort.cpp index 872657a..62ded18 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -391,21 +391,22 @@ uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) { - if (length < 4U) + if (length < 5U) return 4U; uint8_t speed = data[0U]; uint16_t frequency = data[1U] * 10U; - uint16_t delay = data[2U] * 10U; - uint8_t level = data[3U]; + uint8_t minTime = data[2U]; + uint16_t delay = data[3U] * 10U; + uint8_t level = data[4U]; char ack[50U]; uint8_t n = 0U; - for (uint8_t i = 4U; i < length; i++, n++) + for (uint8_t i = 5U; i < length; i++, n++) ack[n] = data[i]; ack[n] = '\0'; - fm.setAck(ack, speed, frequency, delay, level); + fm.setAck(ack, speed, frequency, minTime, delay, level); return 0U; } From 3f80248477694f67361664372ee34c70271a019b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 13 Apr 2020 16:15:24 +0200 Subject: [PATCH 004/119] Add Goertzel --- Goertzel.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Goertzel.h | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Goertzel.cpp create mode 100644 Goertzel.h diff --git a/Goertzel.cpp b/Goertzel.cpp new file mode 100644 index 0000000..811fd97 --- /dev/null +++ b/Goertzel.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "Goertzel.h" + +CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : +m_min(0), +m_max(0), +m_processedSamplesCount(0), +m_n(n), +m_window(window),//Window should not be deleted by someone else +m_windowCorr(windowCorr) +{ + m_freqs[0] = f1; + m_freqs[1] = f2; + m_freqs[2] = f3; + + reset(); +} + +void CGoertzel::reset() +{ + ::memset(m_q1s, 0, sizeof(m_q1s)); + ::memset(m_q2s, 0, sizeof(m_q2s)); + m_processedSamplesCount = 0U; + m_max = 0U; + m_min = 0U; +} + +GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) +{ + int scalingFactor = (length / 2) * m_windowCorr; + unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; + GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; + + for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { + + if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; + if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; + + for(uint8_t i = 0; i < 3; i++) { + int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); + m_q2s[i] = m_q1s[i]; + m_q1s[i] = q0; + } + + m_processedSamplesCount++; + //we have collected enough samples, evaluate now, + if(m_processedSamplesCount == m_n) { + if(magnitudesComputed == GR_NOT_READY) { + //however if we already had collected enough samples only keep the magnitudes we computed the first time + int span = m_max - m_min; + for(uint8_t i = 0; i < 3; i++) { + int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic + int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; + + *(magnitudes[i]) = real * real + imag * imag; + } + + magnitudesComputed = GR_READY; + } + reset(); + } + } + + return magnitudesComputed; +} diff --git a/Goertzel.h b/Goertzel.h new file mode 100644 index 0000000..1b15db9 --- /dev/null +++ b/Goertzel.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(GOERTZEL_H) +#define GOERTZEL_H + +#include "Globals.h" + +typedef struct +{ + int sin, cos, coeff; +} TGoertzelParameters; + + +enum GOERTZEL_RESULT : uint8_t +{ + GR_NOT_READY = 0, + GR_READY = 1 +}; + +class CGoertzel { +public: + //f1,f2,f3 are natural frequencies + CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); + + void reset(); + GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); + +private: + TGoertzelParameters m_freqs[3]; + + q15_t m_min; + q15_t m_max; + + q15_t m_omegas[3]; + int m_sines[3]; + int m_cosines[3]; + int m_coeffs[3]; + + int m_q1s[3]; + int m_q2s[3]; + + uint16_t m_processedSamplesCount; + uint16_t m_n; + + const int* m_window; + int m_windowCorr; +}; + +#endif From 031563e72780b30b46303446c706a8c9368c1e87 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 14 Apr 2020 12:16:07 +0200 Subject: [PATCH 005/119] add CTCSS decoder --- CTCSSDecoder.cpp | 46 ++++++++ CTCSSDecoder.h | 112 ++++++++++++++++++++ HannWindow.h | 268 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 CTCSSDecoder.cpp create mode 100644 CTCSSDecoder.h create mode 100644 HannWindow.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp new file mode 100644 index 0000000..8905b2b --- /dev/null +++ b/CTCSSDecoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "CTCSSDecoder.h" + +CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : +m_thresholdSquared(threshold * threshold), +m_hasCTCSS(false) +{ + m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); +} + + +void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) +{ + unsigned int f1MagSquared, f2MagSquared, f3MagSquared; + GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); + + if(result == GR_READY) { + // Goertzel says it has something for us, so checkt it + // make sure the strongest tone is the wanted one and not the neighbouring tone + m_hasCTCSS = f2MagSquared >= m_thresholdSquared + && f2MagSquared > f1MagSquared + && f2MagSquared > f3MagSquared; + } +} + +bool CCTCSSDEcoder::hasCTCSS() +{ + return m_hasCTCSS; +} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h new file mode 100644 index 0000000..cf9e5e4 --- /dev/null +++ b/CTCSSDecoder.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(CTCSSDECODER_H) +#define CTCSSDECODER_H + +#include "Globals.h" +#include "Goertzel.h" +#include "HannWindow.h" + +#define N_SAMPLES 12000 + +typedef struct +{ + TGoertzelParameters toneLow, tone, toneHi; +} TCTCSSTone; + + +/* + * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. + * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. + * We need in since intermediate values in goertzel will overflow Q15 + */ +const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; +const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; +const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; +const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; +const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; +const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; +const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; +const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; +const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; +const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; +const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; +const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; +const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; +const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; +const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; +const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; +const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; +const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; +const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; +const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; +const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; +const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; +const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; +const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; +const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; +const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; +const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; +const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; +const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; +const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; +const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; +const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; +const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; +const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; +const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; +const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; +const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; +const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; +const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; +const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; +const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; +const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; +const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; +const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; +const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; +const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; +const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; +const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; +const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; +const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; +const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; +const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; +const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; +const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; +const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; + + + +class CCTCSSDEcoder { +public: + //threshold must be 0.0 to 1.0; + CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); + + void samples(const q15_t* samples, uint8_t length); + bool hasCTCSS(); + +private: + unsigned int m_thresholdSquared; + bool m_hasCTCSS; + CGoertzel* m_goertzel; + +}; + +#endif diff --git a/HannWindow.h b/HannWindow.h new file mode 100644 index 0000000..4df9a5e --- /dev/null +++ b/HannWindow.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(HANNWINDOW_H) +#define HANNWINDOW_H + +/* Hann window value as Q15 yet stored into int to avoid overflowing during calculation */ + +const int HANN_WINDOW_CORR = 16383; + +const int HANN_WINDOW[12000] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22, +22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,39,39,40,40,41,42,42,43,43,44,45,45,46,47,47,48,49,49,50, +51,51,52,53,53,54,55,55,56,57,57,58,59,60,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,74,74,75,76,77,78,78,79,80,81,82,83,84,84,85,86,87,88,89, +90,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139, +140,141,142,144,145,146,147,148,149,150,152,153,154,155,156,157,159,160,161,162,163,165,166,167,168,170,171,172,173,175,176,177,178,180,181,182,183,185,186,187,189,190,191,192,194,195,196,198,199,200, +202,203,204,206,207,209,210,211,213,214,215,217,218,220,221,222,224,225,227,228,229,231,232,234,235,237,238,240,241,243,244,245,247,248,250,251,253,254,256,257,259,261,262,264,265,267,268,270,271,273, +274,276,278,279,281,282,284,285,287,289,290,292,293,295,297,298,300,302,303,305,307,308,310,312,313,315,317,318,320,322,323,325,327,328,330,332,334,335,337,339,340,342,344,346,347,349,351,353,355,356, +358,360,362,363,365,367,369,371,373,374,376,378,380,382,383,385,387,389,391,393,395,397,398,400,402,404,406,408,410,412,414,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451, +453,455,457,459,461,463,465,467,469,471,473,475,477,479,481,483,485,487,490,492,494,496,498,500,502,504,506,508,511,513,515,517,519,521,523,526,528,530,532,534,536,539,541,543,545,547,550,552,554,556, +558,561,563,565,567,570,572,574,576,579,581,583,585,588,590,592,594,597,599,601,604,606,608,611,613,615,618,620,622,625,627,629,632,634,636,639,641,643,646,648,651,653,655,658,660,663,665,668,670,672, +675,677,680,682,685,687,690,692,694,697,699,702,704,707,709,712,714,717,719,722,724,727,729,732,735,737,740,742,745,747,750,752,755,758,760,763,765,768,771,773,776,778,781,784,786,789,791,794,797,799, +802,805,807,810,813,815,818,821,823,826,829,831,834,837,840,842,845,848,850,853,856,859,861,864,867,870,872,875,878,881,883,886,889,892,895,897,900,903,906,909,911,914,917,920,923,926,928,931,934,937, +940,943,946,949,951,954,957,960,963,966,969,972,975,978,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085, +1088,1091,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1126,1129,1132,1135,1138,1141,1144,1148,1151,1154,1157,1160,1163,1167,1170,1173,1176,1179,1183,1186,1189,1192,1195,1199,1202,1205,1208,1211,1215,1218,1221,1224,1228,1231,1234,1238,1241,1244, +1247,1251,1254,1257,1261,1264,1267,1270,1274,1277,1280,1284,1287,1290,1294,1297,1300,1304,1307,1310,1314,1317,1321,1324,1327,1331,1334,1338,1341,1344,1348,1351,1355,1358,1361,1365,1368,1372,1375,1379,1382,1385,1389,1392,1396,1399,1403,1406,1410,1413, +1417,1420,1424,1427,1431,1434,1438,1441,1445,1448,1452,1455,1459,1462,1466,1470,1473,1477,1480,1484,1487,1491,1494,1498,1502,1505,1509,1512,1516,1520,1523,1527,1530,1534,1538,1541,1545,1549,1552,1556,1560,1563,1567,1571,1574,1578,1582,1585,1589,1593, +1596,1600,1604,1607,1611,1615,1619,1622,1626,1630,1633,1637,1641,1645,1648,1652,1656,1660,1663,1667,1671,1675,1679,1682,1686,1690,1694,1698,1701,1705,1709,1713,1717,1720,1724,1728,1732,1736,1740,1743,1747,1751,1755,1759,1763,1767,1770,1774,1778,1782, +1786,1790,1794,1798,1802,1806,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893,1897,1901,1905,1909,1913,1917,1921,1925,1929,1933,1937,1941,1945,1949,1953,1957,1961,1965,1969,1974,1978,1982, +1986,1990,1994,1998,2002,2006,2010,2015,2019,2023,2027,2031,2035,2039,2043,2048,2052,2056,2060,2064,2068,2073,2077,2081,2085,2089,2094,2098,2102,2106,2110,2115,2119,2123,2127,2131,2136,2140,2144,2148,2153,2157,2161,2165,2170,2174,2178,2183,2187,2191, +2195,2200,2204,2208,2213,2217,2221,2226,2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2273,2278,2282,2286,2291,2295,2299,2304,2308,2313,2317,2321,2326,2330,2335,2339,2343,2348,2352,2357,2361,2366,2370,2375,2379,2383,2388,2392,2397,2401,2406,2410, +2415,2419,2424,2428,2433,2437,2442,2446,2451,2455,2460,2464,2469,2473,2478,2482,2487,2492,2496,2501,2505,2510,2514,2519,2523,2528,2533,2537,2542,2546,2551,2556,2560,2565,2569,2574,2579,2583,2588,2592,2597,2602,2606,2611,2616,2620,2625,2630,2634,2639, +2644,2648,2653,2658,2662,2667,2672,2676,2681,2686,2691,2695,2700,2705,2709,2714,2719,2724,2728,2733,2738,2743,2747,2752,2757,2762,2766,2771,2776,2781,2786,2790,2795,2800,2805,2810,2814,2819,2824,2829,2834,2838,2843,2848,2853,2858,2863,2867,2872,2877, +2882,2887,2892,2897,2901,2906,2911,2916,2921,2926,2931,2936,2941,2945,2950,2955,2960,2965,2970,2975,2980,2985,2990,2995,3000,3005,3010,3015,3020,3024,3029,3034,3039,3044,3049,3054,3059,3064,3069,3074,3079,3084,3089,3094,3099,3104,3109,3114,3119,3125, +3130,3135,3140,3145,3150,3155,3160,3165,3170,3175,3180,3185,3190,3195,3201,3206,3211,3216,3221,3226,3231,3236,3241,3247,3252,3257,3262,3267,3272,3277,3282,3288,3293,3298,3303,3308,3313,3319,3324,3329,3334,3339,3345,3350,3355,3360,3365,3371,3376,3381, +3386,3391,3397,3402,3407,3412,3418,3423,3428,3433,3439,3444,3449,3454,3460,3465,3470,3476,3481,3486,3491,3497,3502,3507,3513,3518,3523,3529,3534,3539,3545,3550,3555,3561,3566,3571,3577,3582,3587,3593,3598,3603,3609,3614,3619,3625,3630,3636,3641,3646, +3652,3657,3663,3668,3673,3679,3684,3690,3695,3701,3706,3711,3717,3722,3728,3733,3739,3744,3750,3755,3761,3766,3771,3777,3782,3788,3793,3799,3804,3810,3815,3821,3826,3832,3837,3843,3848,3854,3860,3865,3871,3876,3882,3887,3893,3898,3904,3909,3915,3921, +3926,3932,3937,3943,3948,3954,3960,3965,3971,3976,3982,3988,3993,3999,4004,4010,4016,4021,4027,4033,4038,4044,4050,4055,4061,4067,4072,4078,4083,4089,4095,4101,4106,4112,4118,4123,4129,4135,4140,4146,4152,4157,4163,4169,4175,4180,4186,4192,4198,4203, +4209,4215,4220,4226,4232,4238,4243,4249,4255,4261,4267,4272,4278,4284,4290,4295,4301,4307,4313,4319,4324,4330,4336,4342,4348,4354,4359,4365,4371,4377,4383,4389,4394,4400,4406,4412,4418,4424,4430,4435,4441,4447,4453,4459,4465,4471,4477,4482,4488,4494, +4500,4506,4512,4518,4524,4530,4536,4542,4548,4553,4559,4565,4571,4577,4583,4589,4595,4601,4607,4613,4619,4625,4631,4637,4643,4649,4655,4661,4667,4673,4679,4685,4691,4697,4703,4709,4715,4721,4727,4733,4739,4745,4751,4757,4763,4769,4775,4781,4787,4793, +4800,4806,4812,4818,4824,4830,4836,4842,4848,4854,4860,4866,4873,4879,4885,4891,4897,4903,4909,4915,4921,4928,4934,4940,4946,4952,4958,4964,4971,4977,4983,4989,4995,5001,5008,5014,5020,5026,5032,5039,5045,5051,5057,5063,5070,5076,5082,5088,5094,5101, +5107,5113,5119,5125,5132,5138,5144,5150,5157,5163,5169,5175,5182,5188,5194,5201,5207,5213,5219,5226,5232,5238,5244,5251,5257,5263,5270,5276,5282,5289,5295,5301,5308,5314,5320,5327,5333,5339,5346,5352,5358,5365,5371,5377,5384,5390,5396,5403,5409,5415, +5422,5428,5435,5441,5447,5454,5460,5467,5473,5479,5486,5492,5499,5505,5511,5518,5524,5531,5537,5544,5550,5556,5563,5569,5576,5582,5589,5595,5602,5608,5614,5621,5627,5634,5640,5647,5653,5660,5666,5673,5679,5686,5692,5699,5705,5712,5718,5725,5731,5738, +5744,5751,5757,5764,5770,5777,5784,5790,5797,5803,5810,5816,5823,5829,5836,5843,5849,5856,5862,5869,5875,5882,5889,5895,5902,5908,5915,5922,5928,5935,5941,5948,5955,5961,5968,5974,5981,5988,5994,6001,6008,6014,6021,6028,6034,6041,6048,6054,6061,6067, +6074,6081,6088,6094,6101,6108,6114,6121,6128,6134,6141,6148,6154,6161,6168,6174,6181,6188,6195,6201,6208,6215,6222,6228,6235,6242,6248,6255,6262,6269,6275,6282,6289,6296,6302,6309,6316,6323,6330,6336,6343,6350,6357,6363,6370,6377,6384,6391,6397,6404, +6411,6418,6425,6431,6438,6445,6452,6459,6466,6472,6479,6486,6493,6500,6507,6513,6520,6527,6534,6541,6548,6555,6561,6568,6575,6582,6589,6596,6603,6610,6616,6623,6630,6637,6644,6651,6658,6665,6672,6679,6685,6692,6699,6706,6713,6720,6727,6734,6741,6748, +6755,6762,6769,6776,6783,6790,6796,6803,6810,6817,6824,6831,6838,6845,6852,6859,6866,6873,6880,6887,6894,6901,6908,6915,6922,6929,6936,6943,6950,6957,6964,6971,6978,6985,6992,6999,7006,7013,7020,7027,7035,7042,7049,7056,7063,7070,7077,7084,7091,7098, +7105,7112,7119,7126,7133,7140,7148,7155,7162,7169,7176,7183,7190,7197,7204,7211,7219,7226,7233,7240,7247,7254,7261,7268,7276,7283,7290,7297,7304,7311,7318,7326,7333,7340,7347,7354,7361,7368,7376,7383,7390,7397,7404,7411,7419,7426,7433,7440,7447,7455, +7462,7469,7476,7483,7491,7498,7505,7512,7519,7527,7534,7541,7548,7556,7563,7570,7577,7584,7592,7599,7606,7613,7621,7628,7635,7642,7650,7657,7664,7671,7679,7686,7693,7701,7708,7715,7722,7730,7737,7744,7752,7759,7766,7773,7781,7788,7795,7803,7810,7817, +7825,7832,7839,7847,7854,7861,7869,7876,7883,7891,7898,7905,7913,7920,7927,7935,7942,7949,7957,7964,7971,7979,7986,7993,8001,8008,8016,8023,8030,8038,8045,8052,8060,8067,8075,8082,8089,8097,8104,8112,8119,8126,8134,8141,8149,8156,8164,8171,8178,8186, +8193,8201,8208,8216,8223,8230,8238,8245,8253,8260,8268,8275,8283,8290,8297,8305,8312,8320,8327,8335,8342,8350,8357,8365,8372,8380,8387,8395,8402,8410,8417,8425,8432,8440,8447,8455,8462,8470,8477,8485,8492,8500,8507,8515,8522,8530,8537,8545,8552,8560, +8568,8575,8583,8590,8598,8605,8613,8620,8628,8635,8643,8651,8658,8666,8673,8681,8688,8696,8704,8711,8719,8726,8734,8742,8749,8757,8764,8772,8779,8787,8795,8802,8810,8817,8825,8833,8840,8848,8856,8863,8871,8878,8886,8894,8901,8909,8917,8924,8932,8940, +8947,8955,8962,8970,8978,8985,8993,9001,9008,9016,9024,9031,9039,9047,9054,9062,9070,9077,9085,9093,9100,9108,9116,9124,9131,9139,9147,9154,9162,9170,9177,9185,9193,9201,9208,9216,9224,9231,9239,9247,9255,9262,9270,9278,9285,9293,9301,9309,9316,9324, +9332,9340,9347,9355,9363,9371,9378,9386,9394,9402,9409,9417,9425,9433,9440,9448,9456,9464,9472,9479,9487,9495,9503,9511,9518,9526,9534,9542,9549,9557,9565,9573,9581,9588,9596,9604,9612,9620,9628,9635,9643,9651,9659,9667,9674,9682,9690,9698,9706,9714, +9721,9729,9737,9745,9753,9761,9769,9776,9784,9792,9800,9808,9816,9824,9831,9839,9847,9855,9863,9871,9879,9886,9894,9902,9910,9918,9926,9934,9942,9950,9957,9965,9973,9981,9989,9997,10005,10013,10021,10029,10036,10044,10052,10060,10068,10076,10084,10092,10100,10108, +10116,10124,10131,10139,10147,10155,10163,10171,10179,10187,10195,10203,10211,10219,10227,10235,10243,10251,10259,10267,10274,10282,10290,10298,10306,10314,10322,10330,10338,10346,10354,10362,10370,10378,10386,10394,10402,10410,10418,10426,10434,10442,10450,10458,10466,10474,10482,10490,10498,10506, +10514,10522,10530,10538,10546,10554,10562,10570,10578,10586,10594,10602,10610,10618,10626,10634,10642,10650,10658,10667,10675,10683,10691,10699,10707,10715,10723,10731,10739,10747,10755,10763,10771,10779,10787,10795,10803,10811,10820,10828,10836,10844,10852,10860,10868,10876,10884,10892,10900,10908, +10916,10925,10933,10941,10949,10957,10965,10973,10981,10989,10997,11006,11014,11022,11030,11038,11046,11054,11062,11070,11079,11087,11095,11103,11111,11119,11127,11135,11144,11152,11160,11168,11176,11184,11192,11200,11209,11217,11225,11233,11241,11249,11257,11266,11274,11282,11290,11298,11306,11315, +11323,11331,11339,11347,11355,11364,11372,11380,11388,11396,11404,11413,11421,11429,11437,11445,11453,11462,11470,11478,11486,11494,11503,11511,11519,11527,11535,11544,11552,11560,11568,11576,11585,11593,11601,11609,11617,11626,11634,11642,11650,11658,11667,11675,11683,11691,11699,11708,11716,11724, +11732,11741,11749,11757,11765,11774,11782,11790,11798,11806,11815,11823,11831,11839,11848,11856,11864,11872,11881,11889,11897,11905,11914,11922,11930,11938,11947,11955,11963,11971,11980,11988,11996,12005,12013,12021,12029,12038,12046,12054,12062,12071,12079,12087,12096,12104,12112,12120,12129,12137, +12145,12154,12162,12170,12178,12187,12195,12203,12212,12220,12228,12236,12245,12253,12261,12270,12278,12286,12295,12303,12311,12320,12328,12336,12344,12353,12361,12369,12378,12386,12394,12403,12411,12419,12428,12436,12444,12453,12461,12469,12478,12486,12494,12503,12511,12519,12528,12536,12544,12553, +12561,12569,12578,12586,12594,12603,12611,12619,12628,12636,12644,12653,12661,12670,12678,12686,12695,12703,12711,12720,12728,12736,12745,12753,12762,12770,12778,12787,12795,12803,12812,12820,12828,12837,12845,12854,12862,12870,12879,12887,12896,12904,12912,12921,12929,12937,12946,12954,12963,12971, +12979,12988,12996,13005,13013,13021,13030,13038,13047,13055,13063,13072,13080,13089,13097,13105,13114,13122,13131,13139,13147,13156,13164,13173,13181,13189,13198,13206,13215,13223,13232,13240,13248,13257,13265,13274,13282,13291,13299,13307,13316,13324,13333,13341,13350,13358,13366,13375,13383,13392, +13400,13409,13417,13425,13434,13442,13451,13459,13468,13476,13485,13493,13501,13510,13518,13527,13535,13544,13552,13561,13569,13577,13586,13594,13603,13611,13620,13628,13637,13645,13654,13662,13670,13679,13687,13696,13704,13713,13721,13730,13738,13747,13755,13764,13772,13781,13789,13797,13806,13814, +13823,13831,13840,13848,13857,13865,13874,13882,13891,13899,13908,13916,13925,13933,13942,13950,13959,13967,13976,13984,13992,14001,14009,14018,14026,14035,14043,14052,14060,14069,14077,14086,14094,14103,14111,14120,14128,14137,14145,14154,14162,14171,14179,14188,14196,14205,14213,14222,14230,14239, +14247,14256,14264,14273,14281,14290,14298,14307,14315,14324,14332,14341,14350,14358,14367,14375,14384,14392,14401,14409,14418,14426,14435,14443,14452,14460,14469,14477,14486,14494,14503,14511,14520,14528,14537,14545,14554,14563,14571,14580,14588,14597,14605,14614,14622,14631,14639,14648,14656,14665, +14673,14682,14690,14699,14708,14716,14725,14733,14742,14750,14759,14767,14776,14784,14793,14801,14810,14819,14827,14836,14844,14853,14861,14870,14878,14887,14895,14904,14912,14921,14930,14938,14947,14955,14964,14972,14981,14989,14998,15006,15015,15024,15032,15041,15049,15058,15066,15075,15083,15092, +15101,15109,15118,15126,15135,15143,15152,15160,15169,15178,15186,15195,15203,15212,15220,15229,15237,15246,15255,15263,15272,15280,15289,15297,15306,15314,15323,15332,15340,15349,15357,15366,15374,15383,15392,15400,15409,15417,15426,15434,15443,15451,15460,15469,15477,15486,15494,15503,15511,15520, +15529,15537,15546,15554,15563,15571,15580,15589,15597,15606,15614,15623,15631,15640,15649,15657,15666,15674,15683,15691,15700,15709,15717,15726,15734,15743,15751,15760,15769,15777,15786,15794,15803,15811,15820,15829,15837,15846,15854,15863,15871,15880,15889,15897,15906,15914,15923,15931,15940,15949, +15957,15966,15974,15983,15992,16000,16009,16017,16026,16034,16043,16052,16060,16069,16077,16086,16094,16103,16112,16120,16129,16137,16146,16155,16163,16172,16180,16189,16197,16206,16215,16223,16232,16240,16249,16257,16266,16275,16283,16292,16300,16309,16318,16326,16335,16343,16352,16360,16369,16378, +16386,16395,16403,16412,16420,16429,16438,16446,16455,16463,16472,16481,16489,16498,16506,16515,16523,16532,16541,16549,16558,16566,16575,16583,16592,16601,16609,16618,16626,16635,16644,16652,16661,16669,16678,16686,16695,16704,16712,16721,16729,16738,16746,16755,16764,16772,16781,16789,16798,16806, +16815,16824,16832,16841,16849,16858,16867,16875,16884,16892,16901,16909,16918,16927,16935,16944,16952,16961,16969,16978,16987,16995,17004,17012,17021,17029,17038,17047,17055,17064,17072,17081,17089,17098,17107,17115,17124,17132,17141,17149,17158,17167,17175,17184,17192,17201,17209,17218,17227,17235, +17244,17252,17261,17269,17278,17287,17295,17304,17312,17321,17329,17338,17346,17355,17364,17372,17381,17389,17398,17406,17415,17424,17432,17441,17449,17458,17466,17475,17483,17492,17501,17509,17518,17526,17535,17543,17552,17561,17569,17578,17586,17595,17603,17612,17620,17629,17638,17646,17655,17663, +17672,17680,17689,17697,17706,17714,17723,17732,17740,17749,17757,17766,17774,17783,17791,17800,17809,17817,17826,17834,17843,17851,17860,17868,17877,17885,17894,17903,17911,17920,17928,17937,17945,17954,17962,17971,17979,17988,17996,18005,18014,18022,18031,18039,18048,18056,18065,18073,18082,18090, +18099,18107,18116,18124,18133,18142,18150,18159,18167,18176,18184,18193,18201,18210,18218,18227,18235,18244,18252,18261,18269,18278,18286,18295,18304,18312,18321,18329,18338,18346,18355,18363,18372,18380,18389,18397,18406,18414,18423,18431,18440,18448,18457,18465,18474,18482,18491,18499,18508,18516, +18525,18533,18542,18550,18559,18567,18576,18584,18593,18601,18610,18618,18627,18635,18644,18652,18661,18669,18678,18686,18695,18703,18712,18720,18729,18737,18746,18754,18763,18771,18780,18788,18797,18805,18814,18822,18831,18839,18848,18856,18865,18873,18882,18890,18898,18907,18915,18924,18932,18941, +18949,18958,18966,18975,18983,18992,19000,19009,19017,19026,19034,19043,19051,19059,19068,19076,19085,19093,19102,19110,19119,19127,19136,19144,19153,19161,19169,19178,19186,19195,19203,19212,19220,19229,19237,19245,19254,19262,19271,19279,19288,19296,19305,19313,19321,19330,19338,19347,19355,19364, +19372,19381,19389,19397,19406,19414,19423,19431,19440,19448,19456,19465,19473,19482,19490,19499,19507,19515,19524,19532,19541,19549,19557,19566,19574,19583,19591,19600,19608,19616,19625,19633,19642,19650,19658,19667,19675,19684,19692,19700,19709,19717,19726,19734,19742,19751,19759,19768,19776,19784, +19793,19801,19810,19818,19826,19835,19843,19852,19860,19868,19877,19885,19893,19902,19910,19919,19927,19935,19944,19952,19960,19969,19977,19986,19994,20002,20011,20019,20027,20036,20044,20052,20061,20069,20078,20086,20094,20103,20111,20119,20128,20136,20144,20153,20161,20169,20178,20186,20194,20203, +20211,20220,20228,20236,20245,20253,20261,20270,20278,20286,20295,20303,20311,20320,20328,20336,20345,20353,20361,20369,20378,20386,20394,20403,20411,20419,20428,20436,20444,20453,20461,20469,20478,20486,20494,20502,20511,20519,20527,20536,20544,20552,20561,20569,20577,20585,20594,20602,20610,20619, +20627,20635,20643,20652,20660,20668,20677,20685,20693,20701,20710,20718,20726,20735,20743,20751,20759,20768,20776,20784,20792,20801,20809,20817,20825,20834,20842,20850,20858,20867,20875,20883,20891,20900,20908,20916,20924,20933,20941,20949,20957,20966,20974,20982,20990,20999,21007,21015,21023,21032, +21040,21048,21056,21064,21073,21081,21089,21097,21106,21114,21122,21130,21138,21147,21155,21163,21171,21179,21188,21196,21204,21212,21220,21229,21237,21245,21253,21261,21270,21278,21286,21294,21302,21310,21319,21327,21335,21343,21351,21360,21368,21376,21384,21392,21400,21409,21417,21425,21433,21441, +21449,21458,21466,21474,21482,21490,21498,21506,21515,21523,21531,21539,21547,21555,21563,21572,21580,21588,21596,21604,21612,21620,21629,21637,21645,21653,21661,21669,21677,21685,21694,21702,21710,21718,21726,21734,21742,21750,21758,21767,21775,21783,21791,21799,21807,21815,21823,21831,21839,21847, +21856,21864,21872,21880,21888,21896,21904,21912,21920,21928,21936,21944,21952,21961,21969,21977,21985,21993,22001,22009,22017,22025,22033,22041,22049,22057,22065,22073,22081,22089,22097,22106,22114,22122,22130,22138,22146,22154,22162,22170,22178,22186,22194,22202,22210,22218,22226,22234,22242,22250, +22258,22266,22274,22282,22290,22298,22306,22314,22322,22330,22338,22346,22354,22362,22370,22378,22386,22394,22402,22410,22418,22426,22434,22442,22450,22458,22466,22474,22482,22490,22498,22505,22513,22521,22529,22537,22545,22553,22561,22569,22577,22585,22593,22601,22609,22617,22625,22633,22641,22648, +22656,22664,22672,22680,22688,22696,22704,22712,22720,22728,22736,22743,22751,22759,22767,22775,22783,22791,22799,22807,22815,22822,22830,22838,22846,22854,22862,22870,22878,22885,22893,22901,22909,22917,22925,22933,22941,22948,22956,22964,22972,22980,22988,22996,23003,23011,23019,23027,23035,23043, +23050,23058,23066,23074,23082,23090,23097,23105,23113,23121,23129,23137,23144,23152,23160,23168,23176,23183,23191,23199,23207,23215,23222,23230,23238,23246,23254,23261,23269,23277,23285,23293,23300,23308,23316,23324,23331,23339,23347,23355,23362,23370,23378,23386,23393,23401,23409,23417,23424,23432, +23440,23448,23455,23463,23471,23479,23486,23494,23502,23510,23517,23525,23533,23540,23548,23556,23564,23571,23579,23587,23594,23602,23610,23618,23625,23633,23641,23648,23656,23664,23671,23679,23687,23694,23702,23710,23717,23725,23733,23740,23748,23756,23763,23771,23779,23786,23794,23802,23809,23817, +23825,23832,23840,23848,23855,23863,23870,23878,23886,23893,23901,23909,23916,23924,23931,23939,23947,23954,23962,23970,23977,23985,23992,24000,24008,24015,24023,24030,24038,24045,24053,24061,24068,24076,24083,24091,24098,24106,24114,24121,24129,24136,24144,24151,24159,24167,24174,24182,24189,24197, +24204,24212,24219,24227,24234,24242,24249,24257,24264,24272,24280,24287,24295,24302,24310,24317,24325,24332,24340,24347,24355,24362,24370,24377,24385,24392,24400,24407,24414,24422,24429,24437,24444,24452,24459,24467,24474,24482,24489,24497,24504,24512,24519,24526,24534,24541,24549,24556,24564,24571, +24578,24586,24593,24601,24608,24616,24623,24630,24638,24645,24653,24660,24667,24675,24682,24690,24697,24704,24712,24719,24727,24734,24741,24749,24756,24763,24771,24778,24786,24793,24800,24808,24815,24822,24830,24837,24844,24852,24859,24866,24874,24881,24888,24896,24903,24910,24918,24925,24932,24940, +24947,24954,24962,24969,24976,24984,24991,24998,25006,25013,25020,25027,25035,25042,25049,25057,25064,25071,25078,25086,25093,25100,25107,25115,25122,25129,25136,25144,25151,25158,25165,25173,25180,25187,25194,25202,25209,25216,25223,25231,25238,25245,25252,25259,25267,25274,25281,25288,25295,25303, +25310,25317,25324,25331,25339,25346,25353,25360,25367,25374,25382,25389,25396,25403,25410,25417,25425,25432,25439,25446,25453,25460,25468,25475,25482,25489,25496,25503,25510,25517,25525,25532,25539,25546,25553,25560,25567,25574,25581,25589,25596,25603,25610,25617,25624,25631,25638,25645,25652,25659, +25666,25674,25681,25688,25695,25702,25709,25716,25723,25730,25737,25744,25751,25758,25765,25772,25779,25786,25793,25800,25807,25814,25821,25828,25835,25842,25849,25856,25863,25870,25877,25884,25891,25898,25905,25912,25919,25926,25933,25940,25947,25954,25961,25968,25975,25982,25989,25996,26003,26010, +26017,26024,26031,26038,26044,26051,26058,26065,26072,26079,26086,26093,26100,26107,26114,26121,26127,26134,26141,26148,26155,26162,26169,26176,26182,26189,26196,26203,26210,26217,26224,26231,26237,26244,26251,26258,26265,26272,26278,26285,26292,26299,26306,26313,26319,26326,26333,26340,26347,26354, +26360,26367,26374,26381,26388,26394,26401,26408,26415,26421,26428,26435,26442,26449,26455,26462,26469,26476,26482,26489,26496,26503,26509,26516,26523,26530,26536,26543,26550,26557,26563,26570,26577,26583,26590,26597,26604,26610,26617,26624,26630,26637,26644,26650,26657,26664,26670,26677,26684,26691, +26697,26704,26711,26717,26724,26730,26737,26744,26750,26757,26764,26770,26777,26784,26790,26797,26803,26810,26817,26823,26830,26837,26843,26850,26856,26863,26870,26876,26883,26889,26896,26902,26909,26916,26922,26929,26935,26942,26948,26955,26962,26968,26975,26981,26988,26994,27001,27007,27014,27020, +27027,27033,27040,27046,27053,27059,27066,27073,27079,27086,27092,27098,27105,27111,27118,27124,27131,27137,27144,27150,27157,27163,27170,27176,27183,27189,27196,27202,27208,27215,27221,27228,27234,27241,27247,27253,27260,27266,27273,27279,27285,27292,27298,27305,27311,27317,27324,27330,27337,27343, +27349,27356,27362,27368,27375,27381,27388,27394,27400,27407,27413,27419,27426,27432,27438,27445,27451,27457,27464,27470,27476,27483,27489,27495,27501,27508,27514,27520,27527,27533,27539,27546,27552,27558,27564,27571,27577,27583,27589,27596,27602,27608,27614,27621,27627,27633,27639,27646,27652,27658, +27664,27671,27677,27683,27689,27695,27702,27708,27714,27720,27726,27733,27739,27745,27751,27757,27763,27770,27776,27782,27788,27794,27800,27807,27813,27819,27825,27831,27837,27843,27850,27856,27862,27868,27874,27880,27886,27892,27898,27905,27911,27917,27923,27929,27935,27941,27947,27953,27959,27965, +27972,27978,27984,27990,27996,28002,28008,28014,28020,28026,28032,28038,28044,28050,28056,28062,28068,28074,28080,28086,28092,28098,28104,28110,28116,28122,28128,28134,28140,28146,28152,28158,28164,28170,28176,28182,28188,28194,28200,28206,28212,28218,28223,28229,28235,28241,28247,28253,28259,28265, +28271,28277,28283,28288,28294,28300,28306,28312,28318,28324,28330,28336,28341,28347,28353,28359,28365,28371,28377,28382,28388,28394,28400,28406,28412,28417,28423,28429,28435,28441,28446,28452,28458,28464,28470,28475,28481,28487,28493,28499,28504,28510,28516,28522,28527,28533,28539,28545,28550,28556, +28562,28568,28573,28579,28585,28591,28596,28602,28608,28613,28619,28625,28631,28636,28642,28648,28653,28659,28665,28670,28676,28682,28687,28693,28699,28704,28710,28716,28721,28727,28733,28738,28744,28749,28755,28761,28766,28772,28778,28783,28789,28794,28800,28806,28811,28817,28822,28828,28834,28839, +28845,28850,28856,28861,28867,28872,28878,28884,28889,28895,28900,28906,28911,28917,28922,28928,28933,28939,28944,28950,28955,28961,28966,28972,28977,28983,28988,28994,28999,29005,29010,29016,29021,29027,29032,29038,29043,29048,29054,29059,29065,29070,29076,29081,29086,29092,29097,29103,29108,29113, +29119,29124,29130,29135,29140,29146,29151,29157,29162,29167,29173,29178,29183,29189,29194,29199,29205,29210,29215,29221,29226,29231,29237,29242,29247,29253,29258,29263,29269,29274,29279,29285,29290,29295,29300,29306,29311,29316,29321,29327,29332,29337,29342,29348,29353,29358,29363,29369,29374,29379, +29384,29390,29395,29400,29405,29410,29416,29421,29426,29431,29436,29442,29447,29452,29457,29462,29467,29473,29478,29483,29488,29493,29498,29504,29509,29514,29519,29524,29529,29534,29539,29545,29550,29555,29560,29565,29570,29575,29580,29585,29590,29595,29601,29606,29611,29616,29621,29626,29631,29636, +29641,29646,29651,29656,29661,29666,29671,29676,29681,29686,29691,29696,29701,29706,29711,29716,29721,29726,29731,29736,29741,29746,29751,29756,29761,29766,29771,29776,29781,29786,29791,29795,29800,29805,29810,29815,29820,29825,29830,29835,29840,29845,29849,29854,29859,29864,29869,29874,29879,29884, +29888,29893,29898,29903,29908,29913,29918,29922,29927,29932,29937,29942,29946,29951,29956,29961,29966,29970,29975,29980,29985,29990,29994,29999,30004,30009,30013,30018,30023,30028,30033,30037,30042,30047,30051,30056,30061,30066,30070,30075,30080,30084,30089,30094,30099,30103,30108,30113,30117,30122, +30127,30131,30136,30141,30145,30150,30155,30159,30164,30169,30173,30178,30182,30187,30192,30196,30201,30206,30210,30215,30219,30224,30229,30233,30238,30242,30247,30251,30256,30261,30265,30270,30274,30279,30283,30288,30292,30297,30301,30306,30311,30315,30320,30324,30329,30333,30338,30342,30347,30351, +30356,30360,30364,30369,30373,30378,30382,30387,30391,30396,30400,30405,30409,30413,30418,30422,30427,30431,30436,30440,30444,30449,30453,30458,30462,30466,30471,30475,30479,30484,30488,30493,30497,30501,30506,30510,30514,30519,30523,30527,30532,30536,30540,30545,30549,30553,30558,30562,30566,30570, +30575,30579,30583,30588,30592,30596,30600,30605,30609,30613,30617,30622,30626,30630,30634,30639,30643,30647,30651,30656,30660,30664,30668,30672,30677,30681,30685,30689,30693,30697,30702,30706,30710,30714,30718,30722,30727,30731,30735,30739,30743,30747,30751,30756,30760,30764,30768,30772,30776,30780, +30784,30788,30792,30797,30801,30805,30809,30813,30817,30821,30825,30829,30833,30837,30841,30845,30849,30853,30857,30861,30865,30869,30873,30877,30881,30885,30889,30893,30897,30901,30905,30909,30913,30917,30921,30925,30929,30933,30937,30941,30945,30949,30953,30957,30960,30964,30968,30972,30976,30980, +30984,30988,30992,30996,30999,31003,31007,31011,31015,31019,31023,31026,31030,31034,31038,31042,31046,31050,31053,31057,31061,31065,31069,31072,31076,31080,31084,31088,31091,31095,31099,31103,31106,31110,31114,31118,31121,31125,31129,31133,31136,31140,31144,31148,31151,31155,31159,31162,31166,31170, +31174,31177,31181,31185,31188,31192,31196,31199,31203,31207,31210,31214,31218,31221,31225,31228,31232,31236,31239,31243,31247,31250,31254,31257,31261,31265,31268,31272,31275,31279,31282,31286,31290,31293,31297,31300,31304,31307,31311,31314,31318,31321,31325,31329,31332,31336,31339,31343,31346,31350, +31353,31357,31360,31363,31367,31370,31374,31377,31381,31384,31388,31391,31395,31398,31401,31405,31408,31412,31415,31419,31422,31425,31429,31432,31436,31439,31442,31446,31449,31452,31456,31459,31463,31466,31469,31473,31476,31479,31483,31486,31489,31493,31496,31499,31503,31506,31509,31512,31516,31519, +31522,31526,31529,31532,31535,31539,31542,31545,31548,31552,31555,31558,31561,31565,31568,31571,31574,31577,31581,31584,31587,31590,31593,31597,31600,31603,31606,31609,31613,31616,31619,31622,31625,31628,31631,31635,31638,31641,31644,31647,31650,31653,31656,31660,31663,31666,31669,31672,31675,31678, +31681,31684,31687,31690,31693,31696,31700,31703,31706,31709,31712,31715,31718,31721,31724,31727,31730,31733,31736,31739,31742,31745,31748,31751,31754,31757,31760,31763,31766,31768,31771,31774,31777,31780,31783,31786,31789,31792,31795,31798,31801,31804,31806,31809,31812,31815,31818,31821,31824,31827, +31830,31832,31835,31838,31841,31844,31847,31849,31852,31855,31858,31861,31864,31866,31869,31872,31875,31878,31880,31883,31886,31889,31891,31894,31897,31900,31902,31905,31908,31911,31913,31916,31919,31922,31924,31927,31930,31933,31935,31938,31941,31943,31946,31949,31951,31954,31957,31959,31962,31965, +31967,31970,31973,31975,31978,31980,31983,31986,31988,31991,31994,31996,31999,32001,32004,32007,32009,32012,32014,32017,32019,32022,32025,32027,32030,32032,32035,32037,32040,32042,32045,32047,32050,32052,32055,32057,32060,32062,32065,32067,32070,32072,32075,32077,32080,32082,32085,32087,32090,32092, +32094,32097,32099,32102,32104,32107,32109,32111,32114,32116,32119,32121,32123,32126,32128,32130,32133,32135,32138,32140,32142,32145,32147,32149,32152,32154,32156,32159,32161,32163,32166,32168,32170,32172,32175,32177,32179,32182,32184,32186,32188,32191,32193,32195,32197,32200,32202,32204,32206,32209, +32211,32213,32215,32217,32220,32222,32224,32226,32228,32231,32233,32235,32237,32239,32241,32244,32246,32248,32250,32252,32254,32256,32259,32261,32263,32265,32267,32269,32271,32273,32275,32277,32280,32282,32284,32286,32288,32290,32292,32294,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314, +32316,32318,32320,32322,32324,32326,32328,32330,32332,32334,32336,32338,32340,32342,32344,32346,32348,32350,32352,32353,32355,32357,32359,32361,32363,32365,32367,32369,32371,32372,32374,32376,32378,32380,32382,32384,32385,32387,32389,32391,32393,32395,32396,32398,32400,32402,32404,32405,32407,32409, +32411,32413,32414,32416,32418,32420,32421,32423,32425,32427,32428,32430,32432,32434,32435,32437,32439,32440,32442,32444,32446,32447,32449,32451,32452,32454,32456,32457,32459,32461,32462,32464,32466,32467,32469,32470,32472,32474,32475,32477,32479,32480,32482,32483,32485,32487,32488,32490,32491,32493, +32494,32496,32497,32499,32501,32502,32504,32505,32507,32508,32510,32511,32513,32514,32516,32517,32519,32520,32522,32523,32525,32526,32528,32529,32531,32532,32533,32535,32536,32538,32539,32541,32542,32544,32545,32546,32548,32549,32551,32552,32553,32555,32556,32557,32559,32560,32562,32563,32564,32566, +32567,32568,32570,32571,32572,32574,32575,32576,32578,32579,32580,32581,32583,32584,32585,32587,32588,32589,32590,32592,32593,32594,32595,32597,32598,32599,32600,32601,32603,32604,32605,32606,32608,32609,32610,32611,32612,32613,32615,32616,32617,32618,32619,32620,32622,32623,32624,32625,32626,32627, +32628,32629,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32670,32671,32672,32673,32674,32675,32676,32677,32678, +32679,32680,32680,32681,32682,32683,32684,32685,32686,32687,32687,32688,32689,32690,32691,32692,32692,32693,32694,32695,32696,32696,32697,32698,32699,32700,32700,32701,32702,32703,32704,32704,32705,32706,32707,32707,32708,32709,32709,32710,32711,32712,32712,32713,32714,32714,32715,32716,32716,32717, +32718,32718,32719,32720,32720,32721,32722,32722,32723,32724,32724,32725,32726,32726,32727,32727,32728,32729,32729,32730,32730,32731,32731,32732,32733,32733,32734,32734,32735,32735,32736,32736,32737,32738,32738,32739,32739,32740,32740,32741,32741,32742,32742,32743,32743,32743,32744,32744,32745,32745, +32746,32746,32747,32747,32748,32748,32748,32749,32749,32750,32750,32750,32751,32751,32752,32752,32752,32753,32753,32753,32754,32754,32755,32755,32755,32756,32756,32756,32757,32757,32757,32757,32758,32758,32758,32759,32759,32759,32760,32760,32760,32760,32761,32761,32761,32761,32762,32762,32762,32762, +32762,32763,32763,32763,32763,32764,32764,32764,32764,32764,32764,32765,32765,32765,32765,32765,32765,32766,32766,32766,32766,32766,32766,32766,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768, +32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32766,32766,32766,32766,32766,32766,32766,32765,32765,32765,32765,32765,32765,32764,32764,32764,32764,32764,32764,32763,32763,32763,32763,32762, +32762,32762,32762,32762,32761,32761,32761,32761,32760,32760,32760,32760,32759,32759,32759,32758,32758,32758,32757,32757,32757,32757,32756,32756,32756,32755,32755,32755,32754,32754,32753,32753,32753,32752,32752,32752,32751,32751,32750,32750,32750,32749,32749,32748,32748,32748,32747,32747,32746,32746, +32745,32745,32744,32744,32743,32743,32743,32742,32742,32741,32741,32740,32740,32739,32739,32738,32738,32737,32736,32736,32735,32735,32734,32734,32733,32733,32732,32731,32731,32730,32730,32729,32729,32728,32727,32727,32726,32726,32725,32724,32724,32723,32722,32722,32721,32720,32720,32719,32718,32718, +32717,32716,32716,32715,32714,32714,32713,32712,32712,32711,32710,32709,32709,32708,32707,32707,32706,32705,32704,32704,32703,32702,32701,32700,32700,32699,32698,32697,32696,32696,32695,32694,32693,32692,32692,32691,32690,32689,32688,32687,32687,32686,32685,32684,32683,32682,32681,32680,32680,32679, +32678,32677,32676,32675,32674,32673,32672,32671,32670,32670,32669,32668,32667,32666,32665,32664,32663,32662,32661,32660,32659,32658,32657,32656,32655,32654,32653,32652,32651,32650,32649,32648,32647,32646,32645,32644,32643,32641,32640,32639,32638,32637,32636,32635,32634,32633,32632,32631,32629,32628, +32627,32626,32625,32624,32623,32622,32620,32619,32618,32617,32616,32615,32613,32612,32611,32610,32609,32608,32606,32605,32604,32603,32601,32600,32599,32598,32597,32595,32594,32593,32592,32590,32589,32588,32587,32585,32584,32583,32581,32580,32579,32578,32576,32575,32574,32572,32571,32570,32568,32567, +32566,32564,32563,32562,32560,32559,32557,32556,32555,32553,32552,32551,32549,32548,32546,32545,32544,32542,32541,32539,32538,32536,32535,32533,32532,32531,32529,32528,32526,32525,32523,32522,32520,32519,32517,32516,32514,32513,32511,32510,32508,32507,32505,32504,32502,32501,32499,32497,32496,32494, +32493,32491,32490,32488,32487,32485,32483,32482,32480,32479,32477,32475,32474,32472,32470,32469,32467,32466,32464,32462,32461,32459,32457,32456,32454,32452,32451,32449,32447,32446,32444,32442,32440,32439,32437,32435,32434,32432,32430,32428,32427,32425,32423,32421,32420,32418,32416,32414,32413,32411, +32409,32407,32405,32404,32402,32400,32398,32396,32395,32393,32391,32389,32387,32385,32384,32382,32380,32378,32376,32374,32372,32371,32369,32367,32365,32363,32361,32359,32357,32355,32353,32352,32350,32348,32346,32344,32342,32340,32338,32336,32334,32332,32330,32328,32326,32324,32322,32320,32318,32316, +32314,32312,32310,32308,32306,32304,32302,32300,32298,32296,32294,32292,32290,32288,32286,32284,32282,32280,32277,32275,32273,32271,32269,32267,32265,32263,32261,32259,32256,32254,32252,32250,32248,32246,32244,32241,32239,32237,32235,32233,32231,32228,32226,32224,32222,32220,32217,32215,32213,32211, +32209,32206,32204,32202,32200,32197,32195,32193,32191,32188,32186,32184,32182,32179,32177,32175,32172,32170,32168,32166,32163,32161,32159,32156,32154,32152,32149,32147,32145,32142,32140,32138,32135,32133,32130,32128,32126,32123,32121,32119,32116,32114,32111,32109,32107,32104,32102,32099,32097,32094, +32092,32090,32087,32085,32082,32080,32077,32075,32072,32070,32067,32065,32062,32060,32057,32055,32052,32050,32047,32045,32042,32040,32037,32035,32032,32030,32027,32025,32022,32019,32017,32014,32012,32009,32007,32004,32001,31999,31996,31994,31991,31988,31986,31983,31980,31978,31975,31973,31970,31967, +31965,31962,31959,31957,31954,31951,31949,31946,31943,31941,31938,31935,31933,31930,31927,31924,31922,31919,31916,31913,31911,31908,31905,31902,31900,31897,31894,31891,31889,31886,31883,31880,31878,31875,31872,31869,31866,31864,31861,31858,31855,31852,31849,31847,31844,31841,31838,31835,31832,31830, +31827,31824,31821,31818,31815,31812,31809,31806,31804,31801,31798,31795,31792,31789,31786,31783,31780,31777,31774,31771,31768,31766,31763,31760,31757,31754,31751,31748,31745,31742,31739,31736,31733,31730,31727,31724,31721,31718,31715,31712,31709,31706,31703,31700,31696,31693,31690,31687,31684,31681, +31678,31675,31672,31669,31666,31663,31660,31656,31653,31650,31647,31644,31641,31638,31635,31631,31628,31625,31622,31619,31616,31613,31609,31606,31603,31600,31597,31593,31590,31587,31584,31581,31577,31574,31571,31568,31565,31561,31558,31555,31552,31548,31545,31542,31539,31535,31532,31529,31526,31522, +31519,31516,31512,31509,31506,31503,31499,31496,31493,31489,31486,31483,31479,31476,31473,31469,31466,31463,31459,31456,31452,31449,31446,31442,31439,31436,31432,31429,31425,31422,31419,31415,31412,31408,31405,31401,31398,31395,31391,31388,31384,31381,31377,31374,31370,31367,31363,31360,31357,31353, +31350,31346,31343,31339,31336,31332,31329,31325,31321,31318,31314,31311,31307,31304,31300,31297,31293,31290,31286,31282,31279,31275,31272,31268,31265,31261,31257,31254,31250,31247,31243,31239,31236,31232,31228,31225,31221,31218,31214,31210,31207,31203,31199,31196,31192,31188,31185,31181,31177,31174, +31170,31166,31162,31159,31155,31151,31148,31144,31140,31136,31133,31129,31125,31121,31118,31114,31110,31106,31103,31099,31095,31091,31088,31084,31080,31076,31072,31069,31065,31061,31057,31053,31050,31046,31042,31038,31034,31030,31026,31023,31019,31015,31011,31007,31003,30999,30996,30992,30988,30984, +30980,30976,30972,30968,30964,30960,30957,30953,30949,30945,30941,30937,30933,30929,30925,30921,30917,30913,30909,30905,30901,30897,30893,30889,30885,30881,30877,30873,30869,30865,30861,30857,30853,30849,30845,30841,30837,30833,30829,30825,30821,30817,30813,30809,30805,30801,30797,30792,30788,30784, +30780,30776,30772,30768,30764,30760,30756,30751,30747,30743,30739,30735,30731,30727,30722,30718,30714,30710,30706,30702,30697,30693,30689,30685,30681,30677,30672,30668,30664,30660,30656,30651,30647,30643,30639,30634,30630,30626,30622,30617,30613,30609,30605,30600,30596,30592,30588,30583,30579,30575, +30570,30566,30562,30558,30553,30549,30545,30540,30536,30532,30527,30523,30519,30514,30510,30506,30501,30497,30493,30488,30484,30479,30475,30471,30466,30462,30458,30453,30449,30444,30440,30436,30431,30427,30422,30418,30413,30409,30405,30400,30396,30391,30387,30382,30378,30373,30369,30364,30360,30356, +30351,30347,30342,30338,30333,30329,30324,30320,30315,30311,30306,30301,30297,30292,30288,30283,30279,30274,30270,30265,30261,30256,30251,30247,30242,30238,30233,30229,30224,30219,30215,30210,30206,30201,30196,30192,30187,30182,30178,30173,30169,30164,30159,30155,30150,30145,30141,30136,30131,30127, +30122,30117,30113,30108,30103,30099,30094,30089,30084,30080,30075,30070,30066,30061,30056,30051,30047,30042,30037,30033,30028,30023,30018,30013,30009,30004,29999,29994,29990,29985,29980,29975,29970,29966,29961,29956,29951,29946,29942,29937,29932,29927,29922,29918,29913,29908,29903,29898,29893,29888, +29884,29879,29874,29869,29864,29859,29854,29849,29845,29840,29835,29830,29825,29820,29815,29810,29805,29800,29795,29791,29786,29781,29776,29771,29766,29761,29756,29751,29746,29741,29736,29731,29726,29721,29716,29711,29706,29701,29696,29691,29686,29681,29676,29671,29666,29661,29656,29651,29646,29641, +29636,29631,29626,29621,29616,29611,29606,29601,29595,29590,29585,29580,29575,29570,29565,29560,29555,29550,29545,29539,29534,29529,29524,29519,29514,29509,29504,29498,29493,29488,29483,29478,29473,29467,29462,29457,29452,29447,29442,29436,29431,29426,29421,29416,29410,29405,29400,29395,29390,29384, +29379,29374,29369,29363,29358,29353,29348,29342,29337,29332,29327,29321,29316,29311,29306,29300,29295,29290,29285,29279,29274,29269,29263,29258,29253,29247,29242,29237,29231,29226,29221,29215,29210,29205,29199,29194,29189,29183,29178,29173,29167,29162,29157,29151,29146,29140,29135,29130,29124,29119, +29113,29108,29103,29097,29092,29086,29081,29076,29070,29065,29059,29054,29048,29043,29038,29032,29027,29021,29016,29010,29005,28999,28994,28988,28983,28977,28972,28966,28961,28955,28950,28944,28939,28933,28928,28922,28917,28911,28906,28900,28895,28889,28884,28878,28872,28867,28861,28856,28850,28845, +28839,28834,28828,28822,28817,28811,28806,28800,28794,28789,28783,28778,28772,28766,28761,28755,28749,28744,28738,28733,28727,28721,28716,28710,28704,28699,28693,28687,28682,28676,28670,28665,28659,28653,28648,28642,28636,28631,28625,28619,28613,28608,28602,28596,28591,28585,28579,28573,28568,28562, +28556,28550,28545,28539,28533,28527,28522,28516,28510,28504,28499,28493,28487,28481,28475,28470,28464,28458,28452,28446,28441,28435,28429,28423,28417,28412,28406,28400,28394,28388,28382,28377,28371,28365,28359,28353,28347,28341,28336,28330,28324,28318,28312,28306,28300,28294,28288,28283,28277,28271, +28265,28259,28253,28247,28241,28235,28229,28223,28218,28212,28206,28200,28194,28188,28182,28176,28170,28164,28158,28152,28146,28140,28134,28128,28122,28116,28110,28104,28098,28092,28086,28080,28074,28068,28062,28056,28050,28044,28038,28032,28026,28020,28014,28008,28002,27996,27990,27984,27978,27972, +27965,27959,27953,27947,27941,27935,27929,27923,27917,27911,27905,27898,27892,27886,27880,27874,27868,27862,27856,27850,27843,27837,27831,27825,27819,27813,27807,27800,27794,27788,27782,27776,27770,27763,27757,27751,27745,27739,27733,27726,27720,27714,27708,27702,27695,27689,27683,27677,27671,27664, +27658,27652,27646,27639,27633,27627,27621,27614,27608,27602,27596,27589,27583,27577,27571,27564,27558,27552,27546,27539,27533,27527,27520,27514,27508,27501,27495,27489,27483,27476,27470,27464,27457,27451,27445,27438,27432,27426,27419,27413,27407,27400,27394,27388,27381,27375,27368,27362,27356,27349, +27343,27337,27330,27324,27317,27311,27305,27298,27292,27285,27279,27273,27266,27260,27253,27247,27241,27234,27228,27221,27215,27208,27202,27196,27189,27183,27176,27170,27163,27157,27150,27144,27137,27131,27124,27118,27111,27105,27098,27092,27086,27079,27073,27066,27059,27053,27046,27040,27033,27027, +27020,27014,27007,27001,26994,26988,26981,26975,26968,26962,26955,26948,26942,26935,26929,26922,26916,26909,26902,26896,26889,26883,26876,26870,26863,26856,26850,26843,26837,26830,26823,26817,26810,26803,26797,26790,26784,26777,26770,26764,26757,26750,26744,26737,26730,26724,26717,26711,26704,26697, +26691,26684,26677,26670,26664,26657,26650,26644,26637,26630,26624,26617,26610,26604,26597,26590,26583,26577,26570,26563,26557,26550,26543,26536,26530,26523,26516,26509,26503,26496,26489,26482,26476,26469,26462,26455,26449,26442,26435,26428,26421,26415,26408,26401,26394,26388,26381,26374,26367,26360, +26354,26347,26340,26333,26326,26319,26313,26306,26299,26292,26285,26278,26272,26265,26258,26251,26244,26237,26231,26224,26217,26210,26203,26196,26189,26182,26176,26169,26162,26155,26148,26141,26134,26127,26121,26114,26107,26100,26093,26086,26079,26072,26065,26058,26051,26044,26038,26031,26024,26017, +26010,26003,25996,25989,25982,25975,25968,25961,25954,25947,25940,25933,25926,25919,25912,25905,25898,25891,25884,25877,25870,25863,25856,25849,25842,25835,25828,25821,25814,25807,25800,25793,25786,25779,25772,25765,25758,25751,25744,25737,25730,25723,25716,25709,25702,25695,25688,25681,25674,25666, +25659,25652,25645,25638,25631,25624,25617,25610,25603,25596,25589,25581,25574,25567,25560,25553,25546,25539,25532,25525,25517,25510,25503,25496,25489,25482,25475,25468,25460,25453,25446,25439,25432,25425,25417,25410,25403,25396,25389,25382,25374,25367,25360,25353,25346,25339,25331,25324,25317,25310, +25303,25295,25288,25281,25274,25267,25259,25252,25245,25238,25231,25223,25216,25209,25202,25194,25187,25180,25173,25165,25158,25151,25144,25136,25129,25122,25115,25107,25100,25093,25086,25078,25071,25064,25057,25049,25042,25035,25027,25020,25013,25006,24998,24991,24984,24976,24969,24962,24954,24947, +24940,24932,24925,24918,24910,24903,24896,24888,24881,24874,24866,24859,24852,24844,24837,24830,24822,24815,24808,24800,24793,24786,24778,24771,24763,24756,24749,24741,24734,24727,24719,24712,24704,24697,24690,24682,24675,24667,24660,24653,24645,24638,24630,24623,24616,24608,24601,24593,24586,24578, +24571,24564,24556,24549,24541,24534,24526,24519,24512,24504,24497,24489,24482,24474,24467,24459,24452,24444,24437,24429,24422,24414,24407,24400,24392,24385,24377,24370,24362,24355,24347,24340,24332,24325,24317,24310,24302,24295,24287,24280,24272,24264,24257,24249,24242,24234,24227,24219,24212,24204, +24197,24189,24182,24174,24167,24159,24151,24144,24136,24129,24121,24114,24106,24098,24091,24083,24076,24068,24061,24053,24045,24038,24030,24023,24015,24008,24000,23992,23985,23977,23970,23962,23954,23947,23939,23931,23924,23916,23909,23901,23893,23886,23878,23870,23863,23855,23848,23840,23832,23825, +23817,23809,23802,23794,23786,23779,23771,23763,23756,23748,23740,23733,23725,23717,23710,23702,23694,23687,23679,23671,23664,23656,23648,23641,23633,23625,23618,23610,23602,23594,23587,23579,23571,23564,23556,23548,23540,23533,23525,23517,23510,23502,23494,23486,23479,23471,23463,23455,23448,23440, +23432,23424,23417,23409,23401,23393,23386,23378,23370,23362,23355,23347,23339,23331,23324,23316,23308,23300,23293,23285,23277,23269,23261,23254,23246,23238,23230,23222,23215,23207,23199,23191,23183,23176,23168,23160,23152,23144,23137,23129,23121,23113,23105,23097,23090,23082,23074,23066,23058,23050, +23043,23035,23027,23019,23011,23003,22996,22988,22980,22972,22964,22956,22948,22941,22933,22925,22917,22909,22901,22893,22885,22878,22870,22862,22854,22846,22838,22830,22822,22815,22807,22799,22791,22783,22775,22767,22759,22751,22743,22736,22728,22720,22712,22704,22696,22688,22680,22672,22664,22656, +22648,22641,22633,22625,22617,22609,22601,22593,22585,22577,22569,22561,22553,22545,22537,22529,22521,22513,22505,22498,22490,22482,22474,22466,22458,22450,22442,22434,22426,22418,22410,22402,22394,22386,22378,22370,22362,22354,22346,22338,22330,22322,22314,22306,22298,22290,22282,22274,22266,22258, +22250,22242,22234,22226,22218,22210,22202,22194,22186,22178,22170,22162,22154,22146,22138,22130,22122,22114,22106,22097,22089,22081,22073,22065,22057,22049,22041,22033,22025,22017,22009,22001,21993,21985,21977,21969,21961,21952,21944,21936,21928,21920,21912,21904,21896,21888,21880,21872,21864,21856, +21847,21839,21831,21823,21815,21807,21799,21791,21783,21775,21767,21758,21750,21742,21734,21726,21718,21710,21702,21694,21685,21677,21669,21661,21653,21645,21637,21629,21620,21612,21604,21596,21588,21580,21572,21563,21555,21547,21539,21531,21523,21515,21506,21498,21490,21482,21474,21466,21458,21449, +21441,21433,21425,21417,21409,21400,21392,21384,21376,21368,21360,21351,21343,21335,21327,21319,21310,21302,21294,21286,21278,21270,21261,21253,21245,21237,21229,21220,21212,21204,21196,21188,21179,21171,21163,21155,21147,21138,21130,21122,21114,21106,21097,21089,21081,21073,21064,21056,21048,21040, +21032,21023,21015,21007,20999,20990,20982,20974,20966,20957,20949,20941,20933,20924,20916,20908,20900,20891,20883,20875,20867,20858,20850,20842,20834,20825,20817,20809,20801,20792,20784,20776,20768,20759,20751,20743,20735,20726,20718,20710,20701,20693,20685,20677,20668,20660,20652,20643,20635,20627, +20619,20610,20602,20594,20585,20577,20569,20561,20552,20544,20536,20527,20519,20511,20502,20494,20486,20478,20469,20461,20453,20444,20436,20428,20419,20411,20403,20394,20386,20378,20369,20361,20353,20345,20336,20328,20320,20311,20303,20295,20286,20278,20270,20261,20253,20245,20236,20228,20220,20211, +20203,20194,20186,20178,20169,20161,20153,20144,20136,20128,20119,20111,20103,20094,20086,20078,20069,20061,20052,20044,20036,20027,20019,20011,20002,19994,19986,19977,19969,19960,19952,19944,19935,19927,19919,19910,19902,19893,19885,19877,19868,19860,19852,19843,19835,19826,19818,19810,19801,19793, +19784,19776,19768,19759,19751,19742,19734,19726,19717,19709,19700,19692,19684,19675,19667,19658,19650,19642,19633,19625,19616,19608,19600,19591,19583,19574,19566,19557,19549,19541,19532,19524,19515,19507,19499,19490,19482,19473,19465,19456,19448,19440,19431,19423,19414,19406,19397,19389,19381,19372, +19364,19355,19347,19338,19330,19321,19313,19305,19296,19288,19279,19271,19262,19254,19245,19237,19229,19220,19212,19203,19195,19186,19178,19169,19161,19153,19144,19136,19127,19119,19110,19102,19093,19085,19076,19068,19059,19051,19043,19034,19026,19017,19009,19000,18992,18983,18975,18966,18958,18949, +18941,18932,18924,18915,18907,18898,18890,18882,18873,18865,18856,18848,18839,18831,18822,18814,18805,18797,18788,18780,18771,18763,18754,18746,18737,18729,18720,18712,18703,18695,18686,18678,18669,18661,18652,18644,18635,18627,18618,18610,18601,18593,18584,18576,18567,18559,18550,18542,18533,18525, +18516,18508,18499,18491,18482,18474,18465,18457,18448,18440,18431,18423,18414,18406,18397,18389,18380,18372,18363,18355,18346,18338,18329,18321,18312,18304,18295,18286,18278,18269,18261,18252,18244,18235,18227,18218,18210,18201,18193,18184,18176,18167,18159,18150,18142,18133,18124,18116,18107,18099, +18090,18082,18073,18065,18056,18048,18039,18031,18022,18014,18005,17996,17988,17979,17971,17962,17954,17945,17937,17928,17920,17911,17903,17894,17885,17877,17868,17860,17851,17843,17834,17826,17817,17809,17800,17791,17783,17774,17766,17757,17749,17740,17732,17723,17714,17706,17697,17689,17680,17672, +17663,17655,17646,17638,17629,17620,17612,17603,17595,17586,17578,17569,17561,17552,17543,17535,17526,17518,17509,17501,17492,17483,17475,17466,17458,17449,17441,17432,17424,17415,17406,17398,17389,17381,17372,17364,17355,17346,17338,17329,17321,17312,17304,17295,17287,17278,17269,17261,17252,17244, +17235,17227,17218,17209,17201,17192,17184,17175,17167,17158,17149,17141,17132,17124,17115,17107,17098,17089,17081,17072,17064,17055,17047,17038,17029,17021,17012,17004,16995,16987,16978,16969,16961,16952,16944,16935,16927,16918,16909,16901,16892,16884,16875,16867,16858,16849,16841,16832,16824,16815, +16806,16798,16789,16781,16772,16764,16755,16746,16738,16729,16721,16712,16704,16695,16686,16678,16669,16661,16652,16644,16635,16626,16618,16609,16601,16592,16583,16575,16566,16558,16549,16541,16532,16523,16515,16506,16498,16489,16481,16472,16463,16455,16446,16438,16429,16420,16412,16403,16395,16386, +16378,16369,16360,16352,16343,16335,16326,16318,16309,16300,16292,16283,16275,16266,16257,16249,16240,16232,16223,16215,16206,16197,16189,16180,16172,16163,16155,16146,16137,16129,16120,16112,16103,16094,16086,16077,16069,16060,16052,16043,16034,16026,16017,16009,16000,15992,15983,15974,15966,15957, +15949,15940,15931,15923,15914,15906,15897,15889,15880,15871,15863,15854,15846,15837,15829,15820,15811,15803,15794,15786,15777,15769,15760,15751,15743,15734,15726,15717,15709,15700,15691,15683,15674,15666,15657,15649,15640,15631,15623,15614,15606,15597,15589,15580,15571,15563,15554,15546,15537,15529, +15520,15511,15503,15494,15486,15477,15469,15460,15451,15443,15434,15426,15417,15409,15400,15392,15383,15374,15366,15357,15349,15340,15332,15323,15314,15306,15297,15289,15280,15272,15263,15255,15246,15237,15229,15220,15212,15203,15195,15186,15178,15169,15160,15152,15143,15135,15126,15118,15109,15101, +15092,15083,15075,15066,15058,15049,15041,15032,15024,15015,15006,14998,14989,14981,14972,14964,14955,14947,14938,14930,14921,14912,14904,14895,14887,14878,14870,14861,14853,14844,14836,14827,14819,14810,14801,14793,14784,14776,14767,14759,14750,14742,14733,14725,14716,14708,14699,14690,14682,14673, +14665,14656,14648,14639,14631,14622,14614,14605,14597,14588,14580,14571,14563,14554,14545,14537,14528,14520,14511,14503,14494,14486,14477,14469,14460,14452,14443,14435,14426,14418,14409,14401,14392,14384,14375,14367,14358,14350,14341,14332,14324,14315,14307,14298,14290,14281,14273,14264,14256,14247, +14239,14230,14222,14213,14205,14196,14188,14179,14171,14162,14154,14145,14137,14128,14120,14111,14103,14094,14086,14077,14069,14060,14052,14043,14035,14026,14018,14009,14001,13992,13984,13976,13967,13959,13950,13942,13933,13925,13916,13908,13899,13891,13882,13874,13865,13857,13848,13840,13831,13823, +13814,13806,13797,13789,13781,13772,13764,13755,13747,13738,13730,13721,13713,13704,13696,13687,13679,13670,13662,13654,13645,13637,13628,13620,13611,13603,13594,13586,13577,13569,13561,13552,13544,13535,13527,13518,13510,13501,13493,13485,13476,13468,13459,13451,13442,13434,13425,13417,13409,13400, +13392,13383,13375,13366,13358,13350,13341,13333,13324,13316,13307,13299,13291,13282,13274,13265,13257,13248,13240,13232,13223,13215,13206,13198,13189,13181,13173,13164,13156,13147,13139,13131,13122,13114,13105,13097,13089,13080,13072,13063,13055,13047,13038,13030,13021,13013,13005,12996,12988,12979, +12971,12963,12954,12946,12937,12929,12921,12912,12904,12896,12887,12879,12870,12862,12854,12845,12837,12828,12820,12812,12803,12795,12787,12778,12770,12762,12753,12745,12736,12728,12720,12711,12703,12695,12686,12678,12670,12661,12653,12644,12636,12628,12619,12611,12603,12594,12586,12578,12569,12561, +12553,12544,12536,12528,12519,12511,12503,12494,12486,12478,12469,12461,12453,12444,12436,12428,12419,12411,12403,12394,12386,12378,12369,12361,12353,12344,12336,12328,12320,12311,12303,12295,12286,12278,12270,12261,12253,12245,12236,12228,12220,12212,12203,12195,12187,12178,12170,12162,12154,12145, +12137,12129,12120,12112,12104,12096,12087,12079,12071,12062,12054,12046,12038,12029,12021,12013,12005,11996,11988,11980,11971,11963,11955,11947,11938,11930,11922,11914,11905,11897,11889,11881,11872,11864,11856,11848,11839,11831,11823,11815,11806,11798,11790,11782,11774,11765,11757,11749,11741,11732, +11724,11716,11708,11699,11691,11683,11675,11667,11658,11650,11642,11634,11626,11617,11609,11601,11593,11585,11576,11568,11560,11552,11544,11535,11527,11519,11511,11503,11494,11486,11478,11470,11462,11453,11445,11437,11429,11421,11413,11404,11396,11388,11380,11372,11364,11355,11347,11339,11331,11323, +11315,11306,11298,11290,11282,11274,11266,11257,11249,11241,11233,11225,11217,11209,11200,11192,11184,11176,11168,11160,11152,11144,11135,11127,11119,11111,11103,11095,11087,11079,11070,11062,11054,11046,11038,11030,11022,11014,11006,10997,10989,10981,10973,10965,10957,10949,10941,10933,10925,10916, +10908,10900,10892,10884,10876,10868,10860,10852,10844,10836,10828,10820,10811,10803,10795,10787,10779,10771,10763,10755,10747,10739,10731,10723,10715,10707,10699,10691,10683,10675,10667,10658,10650,10642,10634,10626,10618,10610,10602,10594,10586,10578,10570,10562,10554,10546,10538,10530,10522,10514, +10506,10498,10490,10482,10474,10466,10458,10450,10442,10434,10426,10418,10410,10402,10394,10386,10378,10370,10362,10354,10346,10338,10330,10322,10314,10306,10298,10290,10282,10274,10267,10259,10251,10243,10235,10227,10219,10211,10203,10195,10187,10179,10171,10163,10155,10147,10139,10131,10124,10116, +10108,10100,10092,10084,10076,10068,10060,10052,10044,10036,10029,10021,10013,10005,9997,9989,9981,9973,9965,9957,9950,9942,9934,9926,9918,9910,9902,9894,9886,9879,9871,9863,9855,9847,9839,9831,9824,9816,9808,9800,9792,9784,9776,9769,9761,9753,9745,9737,9729,9721, +9714,9706,9698,9690,9682,9674,9667,9659,9651,9643,9635,9628,9620,9612,9604,9596,9588,9581,9573,9565,9557,9549,9542,9534,9526,9518,9511,9503,9495,9487,9479,9472,9464,9456,9448,9440,9433,9425,9417,9409,9402,9394,9386,9378,9371,9363,9355,9347,9340,9332, +9324,9316,9309,9301,9293,9285,9278,9270,9262,9255,9247,9239,9231,9224,9216,9208,9201,9193,9185,9177,9170,9162,9154,9147,9139,9131,9124,9116,9108,9100,9093,9085,9077,9070,9062,9054,9047,9039,9031,9024,9016,9008,9001,8993,8985,8978,8970,8962,8955,8947, +8940,8932,8924,8917,8909,8901,8894,8886,8878,8871,8863,8856,8848,8840,8833,8825,8817,8810,8802,8795,8787,8779,8772,8764,8757,8749,8742,8734,8726,8719,8711,8704,8696,8688,8681,8673,8666,8658,8651,8643,8635,8628,8620,8613,8605,8598,8590,8583,8575,8568, +8560,8552,8545,8537,8530,8522,8515,8507,8500,8492,8485,8477,8470,8462,8455,8447,8440,8432,8425,8417,8410,8402,8395,8387,8380,8372,8365,8357,8350,8342,8335,8327,8320,8312,8305,8297,8290,8283,8275,8268,8260,8253,8245,8238,8230,8223,8216,8208,8201,8193, +8186,8178,8171,8164,8156,8149,8141,8134,8126,8119,8112,8104,8097,8089,8082,8075,8067,8060,8052,8045,8038,8030,8023,8016,8008,8001,7993,7986,7979,7971,7964,7957,7949,7942,7935,7927,7920,7913,7905,7898,7891,7883,7876,7869,7861,7854,7847,7839,7832,7825, +7817,7810,7803,7795,7788,7781,7773,7766,7759,7752,7744,7737,7730,7722,7715,7708,7701,7693,7686,7679,7671,7664,7657,7650,7642,7635,7628,7621,7613,7606,7599,7592,7584,7577,7570,7563,7556,7548,7541,7534,7527,7519,7512,7505,7498,7491,7483,7476,7469,7462, +7455,7447,7440,7433,7426,7419,7411,7404,7397,7390,7383,7376,7368,7361,7354,7347,7340,7333,7326,7318,7311,7304,7297,7290,7283,7276,7268,7261,7254,7247,7240,7233,7226,7219,7211,7204,7197,7190,7183,7176,7169,7162,7155,7148,7140,7133,7126,7119,7112,7105, +7098,7091,7084,7077,7070,7063,7056,7049,7042,7035,7027,7020,7013,7006,6999,6992,6985,6978,6971,6964,6957,6950,6943,6936,6929,6922,6915,6908,6901,6894,6887,6880,6873,6866,6859,6852,6845,6838,6831,6824,6817,6810,6803,6796,6790,6783,6776,6769,6762,6755, +6748,6741,6734,6727,6720,6713,6706,6699,6692,6685,6679,6672,6665,6658,6651,6644,6637,6630,6623,6616,6610,6603,6596,6589,6582,6575,6568,6561,6555,6548,6541,6534,6527,6520,6513,6507,6500,6493,6486,6479,6472,6466,6459,6452,6445,6438,6431,6425,6418,6411, +6404,6397,6391,6384,6377,6370,6363,6357,6350,6343,6336,6330,6323,6316,6309,6302,6296,6289,6282,6275,6269,6262,6255,6248,6242,6235,6228,6222,6215,6208,6201,6195,6188,6181,6174,6168,6161,6154,6148,6141,6134,6128,6121,6114,6108,6101,6094,6088,6081,6074, +6067,6061,6054,6048,6041,6034,6028,6021,6014,6008,6001,5994,5988,5981,5974,5968,5961,5955,5948,5941,5935,5928,5922,5915,5908,5902,5895,5889,5882,5875,5869,5862,5856,5849,5843,5836,5829,5823,5816,5810,5803,5797,5790,5784,5777,5770,5764,5757,5751,5744, +5738,5731,5725,5718,5712,5705,5699,5692,5686,5679,5673,5666,5660,5653,5647,5640,5634,5627,5621,5614,5608,5602,5595,5589,5582,5576,5569,5563,5556,5550,5544,5537,5531,5524,5518,5511,5505,5499,5492,5486,5479,5473,5467,5460,5454,5447,5441,5435,5428,5422, +5415,5409,5403,5396,5390,5384,5377,5371,5365,5358,5352,5346,5339,5333,5327,5320,5314,5308,5301,5295,5289,5282,5276,5270,5263,5257,5251,5244,5238,5232,5226,5219,5213,5207,5201,5194,5188,5182,5175,5169,5163,5157,5150,5144,5138,5132,5125,5119,5113,5107, +5101,5094,5088,5082,5076,5070,5063,5057,5051,5045,5039,5032,5026,5020,5014,5008,5001,4995,4989,4983,4977,4971,4964,4958,4952,4946,4940,4934,4928,4921,4915,4909,4903,4897,4891,4885,4879,4873,4866,4860,4854,4848,4842,4836,4830,4824,4818,4812,4806,4800, +4793,4787,4781,4775,4769,4763,4757,4751,4745,4739,4733,4727,4721,4715,4709,4703,4697,4691,4685,4679,4673,4667,4661,4655,4649,4643,4637,4631,4625,4619,4613,4607,4601,4595,4589,4583,4577,4571,4565,4559,4553,4548,4542,4536,4530,4524,4518,4512,4506,4500, +4494,4488,4482,4477,4471,4465,4459,4453,4447,4441,4435,4430,4424,4418,4412,4406,4400,4394,4389,4383,4377,4371,4365,4359,4354,4348,4342,4336,4330,4324,4319,4313,4307,4301,4295,4290,4284,4278,4272,4267,4261,4255,4249,4243,4238,4232,4226,4220,4215,4209, +4203,4198,4192,4186,4180,4175,4169,4163,4157,4152,4146,4140,4135,4129,4123,4118,4112,4106,4101,4095,4089,4083,4078,4072,4067,4061,4055,4050,4044,4038,4033,4027,4021,4016,4010,4004,3999,3993,3988,3982,3976,3971,3965,3960,3954,3948,3943,3937,3932,3926, +3921,3915,3909,3904,3898,3893,3887,3882,3876,3871,3865,3860,3854,3848,3843,3837,3832,3826,3821,3815,3810,3804,3799,3793,3788,3782,3777,3771,3766,3761,3755,3750,3744,3739,3733,3728,3722,3717,3711,3706,3701,3695,3690,3684,3679,3673,3668,3663,3657,3652, +3646,3641,3636,3630,3625,3619,3614,3609,3603,3598,3593,3587,3582,3577,3571,3566,3561,3555,3550,3545,3539,3534,3529,3523,3518,3513,3507,3502,3497,3491,3486,3481,3476,3470,3465,3460,3454,3449,3444,3439,3433,3428,3423,3418,3412,3407,3402,3397,3391,3386, +3381,3376,3371,3365,3360,3355,3350,3345,3339,3334,3329,3324,3319,3313,3308,3303,3298,3293,3288,3282,3277,3272,3267,3262,3257,3252,3247,3241,3236,3231,3226,3221,3216,3211,3206,3201,3195,3190,3185,3180,3175,3170,3165,3160,3155,3150,3145,3140,3135,3130, +3125,3119,3114,3109,3104,3099,3094,3089,3084,3079,3074,3069,3064,3059,3054,3049,3044,3039,3034,3029,3024,3020,3015,3010,3005,3000,2995,2990,2985,2980,2975,2970,2965,2960,2955,2950,2945,2941,2936,2931,2926,2921,2916,2911,2906,2901,2897,2892,2887,2882, +2877,2872,2867,2863,2858,2853,2848,2843,2838,2834,2829,2824,2819,2814,2810,2805,2800,2795,2790,2786,2781,2776,2771,2766,2762,2757,2752,2747,2743,2738,2733,2728,2724,2719,2714,2709,2705,2700,2695,2691,2686,2681,2676,2672,2667,2662,2658,2653,2648,2644, +2639,2634,2630,2625,2620,2616,2611,2606,2602,2597,2592,2588,2583,2579,2574,2569,2565,2560,2556,2551,2546,2542,2537,2533,2528,2523,2519,2514,2510,2505,2501,2496,2492,2487,2482,2478,2473,2469,2464,2460,2455,2451,2446,2442,2437,2433,2428,2424,2419,2415, +2410,2406,2401,2397,2392,2388,2383,2379,2375,2370,2366,2361,2357,2352,2348,2343,2339,2335,2330,2326,2321,2317,2313,2308,2304,2299,2295,2291,2286,2282,2278,2273,2269,2265,2260,2256,2251,2247,2243,2238,2234,2230,2226,2221,2217,2213,2208,2204,2200,2195, +2191,2187,2183,2178,2174,2170,2165,2161,2157,2153,2148,2144,2140,2136,2131,2127,2123,2119,2115,2110,2106,2102,2098,2094,2089,2085,2081,2077,2073,2068,2064,2060,2056,2052,2048,2043,2039,2035,2031,2027,2023,2019,2015,2010,2006,2002,1998,1994,1990,1986, +1982,1978,1974,1969,1965,1961,1957,1953,1949,1945,1941,1937,1933,1929,1925,1921,1917,1913,1909,1905,1901,1897,1893,1889,1885,1881,1877,1873,1869,1865,1861,1857,1853,1849,1845,1841,1837,1833,1829,1825,1821,1817,1813,1809,1806,1802,1798,1794,1790,1786, +1782,1778,1774,1770,1767,1763,1759,1755,1751,1747,1743,1740,1736,1732,1728,1724,1720,1717,1713,1709,1705,1701,1698,1694,1690,1686,1682,1679,1675,1671,1667,1663,1660,1656,1652,1648,1645,1641,1637,1633,1630,1626,1622,1619,1615,1611,1607,1604,1600,1596, +1593,1589,1585,1582,1578,1574,1571,1567,1563,1560,1556,1552,1549,1545,1541,1538,1534,1530,1527,1523,1520,1516,1512,1509,1505,1502,1498,1494,1491,1487,1484,1480,1477,1473,1470,1466,1462,1459,1455,1452,1448,1445,1441,1438,1434,1431,1427,1424,1420,1417, +1413,1410,1406,1403,1399,1396,1392,1389,1385,1382,1379,1375,1372,1368,1365,1361,1358,1355,1351,1348,1344,1341,1338,1334,1331,1327,1324,1321,1317,1314,1310,1307,1304,1300,1297,1294,1290,1287,1284,1280,1277,1274,1270,1267,1264,1261,1257,1254,1251,1247, +1244,1241,1238,1234,1231,1228,1224,1221,1218,1215,1211,1208,1205,1202,1199,1195,1192,1189,1186,1183,1179,1176,1173,1170,1167,1163,1160,1157,1154,1151,1148,1144,1141,1138,1135,1132,1129,1126,1122,1119,1116,1113,1110,1107,1104,1101,1098,1095,1091,1088, +1085,1082,1079,1076,1073,1070,1067,1064,1061,1058,1055,1052,1049,1046,1043,1040,1037,1034,1031,1028,1025,1022,1019,1016,1013,1010,1007,1004,1001,998,995,992,989,986,983,980,978,975,972,969,966,963,960,957,954,951,949,946,943,940, +937,934,931,928,926,923,920,917,914,911,909,906,903,900,897,895,892,889,886,883,881,878,875,872,870,867,864,861,859,856,853,850,848,845,842,840,837,834,831,829,826,823,821,818,815,813,810,807,805,802, +799,797,794,791,789,786,784,781,778,776,773,771,768,765,763,760,758,755,752,750,747,745,742,740,737,735,732,729,727,724,722,719,717,714,712,709,707,704,702,699,697,694,692,690,687,685,682,680,677,675, +672,670,668,665,663,660,658,655,653,651,648,646,643,641,639,636,634,632,629,627,625,622,620,618,615,613,611,608,606,604,601,599,597,594,592,590,588,585,583,581,579,576,574,572,570,567,565,563,561,558, +556,554,552,550,547,545,543,541,539,536,534,532,530,528,526,523,521,519,517,515,513,511,508,506,504,502,500,498,496,494,492,490,487,485,483,481,479,477,475,473,471,469,467,465,463,461,459,457,455,453, +451,449,447,445,443,441,439,437,435,433,431,429,427,425,423,421,419,417,415,414,412,410,408,406,404,402,400,398,397,395,393,391,389,387,385,383,382,380,378,376,374,373,371,369,367,365,363,362,360,358, +356,355,353,351,349,347,346,344,342,340,339,337,335,334,332,330,328,327,325,323,322,320,318,317,315,313,312,310,308,307,305,303,302,300,298,297,295,293,292,290,289,287,285,284,282,281,279,278,276,274, +273,271,270,268,267,265,264,262,261,259,257,256,254,253,251,250,248,247,245,244,243,241,240,238,237,235,234,232,231,229,228,227,225,224,222,221,220,218,217,215,214,213,211,210,209,207,206,204,203,202, +200,199,198,196,195,194,192,191,190,189,187,186,185,183,182,181,180,178,177,176,175,173,172,171,170,168,167,166,165,163,162,161,160,159,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140, +139,138,137,136,135,134,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,92,91,90, +89,88,87,86,85,84,84,83,82,81,80,79,78,78,77,76,75,74,74,73,72,71,70,70,69,68,67,66,66,65,64,63,63,62,61,60,60,59,58,57,57,56,55,55,54,53,53,52,51,51, +50,49,49,48,47,47,46,45,45,44,43,43,42,42,41,40,40,39,39,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,28,27,27,26,26,25,25,24,24,23,23,22, +22,22,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6, +5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +#endif \ No newline at end of file From 7a549e96bad2fdb553703ddec40275c21248fa8a Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 14 Apr 2020 12:23:26 +0100 Subject: [PATCH 006/119] Handle the net ack parameter. --- FM.cpp | 4 ++-- FM.h | 4 ++-- SerialPort.cpp | 12 +++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/FM.cpp b/FM.cpp index f500ccb..2fc7909 100644 --- a/FM.cpp +++ b/FM.cpp @@ -40,10 +40,10 @@ void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, u { } -void CFM::setAck(const char* ack, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) +void CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) { } -void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) +void CFM::setMisc(const char* netAck, uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) { } diff --git a/FM.h b/FM.h index cbbd72b..ec2cb6f 100644 --- a/FM.h +++ b/FM.h @@ -32,8 +32,8 @@ public: void reset(); void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd); - void setAck(const char* ack, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - void setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); + void setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); + void setMisc(const char* netAck, uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: }; diff --git a/SerialPort.cpp b/SerialPort.cpp index 62ded18..a942159 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -391,7 +391,7 @@ uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) { - if (length < 5U) + if (length < 6U) return 4U; uint8_t speed = data[0U]; @@ -413,7 +413,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 7U) + if (length < 8U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -426,7 +426,13 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t kerchunkTime = data[5U]; uint8_t hangTime = data[6U]; - fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); + char ack[50U]; + uint8_t n = 0U; + for (uint8_t i = 7U; i < length; i++, n++) + ack[n] = data[i]; + ack[n] = '\0'; + + fm.setMisc(ack, timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); return 0U; } From 7ab6e51c2c272fbf4d89fecf93110b0be7518e0f Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 13 Apr 2020 16:15:24 +0200 Subject: [PATCH 007/119] Add Goertzel --- Goertzel.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Goertzel.h | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Goertzel.cpp create mode 100644 Goertzel.h diff --git a/Goertzel.cpp b/Goertzel.cpp new file mode 100644 index 0000000..811fd97 --- /dev/null +++ b/Goertzel.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "Goertzel.h" + +CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : +m_min(0), +m_max(0), +m_processedSamplesCount(0), +m_n(n), +m_window(window),//Window should not be deleted by someone else +m_windowCorr(windowCorr) +{ + m_freqs[0] = f1; + m_freqs[1] = f2; + m_freqs[2] = f3; + + reset(); +} + +void CGoertzel::reset() +{ + ::memset(m_q1s, 0, sizeof(m_q1s)); + ::memset(m_q2s, 0, sizeof(m_q2s)); + m_processedSamplesCount = 0U; + m_max = 0U; + m_min = 0U; +} + +GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) +{ + int scalingFactor = (length / 2) * m_windowCorr; + unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; + GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; + + for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { + + if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; + if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; + + for(uint8_t i = 0; i < 3; i++) { + int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); + m_q2s[i] = m_q1s[i]; + m_q1s[i] = q0; + } + + m_processedSamplesCount++; + //we have collected enough samples, evaluate now, + if(m_processedSamplesCount == m_n) { + if(magnitudesComputed == GR_NOT_READY) { + //however if we already had collected enough samples only keep the magnitudes we computed the first time + int span = m_max - m_min; + for(uint8_t i = 0; i < 3; i++) { + int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic + int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; + + *(magnitudes[i]) = real * real + imag * imag; + } + + magnitudesComputed = GR_READY; + } + reset(); + } + } + + return magnitudesComputed; +} diff --git a/Goertzel.h b/Goertzel.h new file mode 100644 index 0000000..1b15db9 --- /dev/null +++ b/Goertzel.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(GOERTZEL_H) +#define GOERTZEL_H + +#include "Globals.h" + +typedef struct +{ + int sin, cos, coeff; +} TGoertzelParameters; + + +enum GOERTZEL_RESULT : uint8_t +{ + GR_NOT_READY = 0, + GR_READY = 1 +}; + +class CGoertzel { +public: + //f1,f2,f3 are natural frequencies + CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); + + void reset(); + GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); + +private: + TGoertzelParameters m_freqs[3]; + + q15_t m_min; + q15_t m_max; + + q15_t m_omegas[3]; + int m_sines[3]; + int m_cosines[3]; + int m_coeffs[3]; + + int m_q1s[3]; + int m_q2s[3]; + + uint16_t m_processedSamplesCount; + uint16_t m_n; + + const int* m_window; + int m_windowCorr; +}; + +#endif From cf616caef956396f7fe15f79fc9442c27340edde Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 14 Apr 2020 12:16:07 +0200 Subject: [PATCH 008/119] add CTCSS decoder --- CTCSSDecoder.cpp | 46 ++++++++ CTCSSDecoder.h | 112 ++++++++++++++++++++ HannWindow.h | 268 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 CTCSSDecoder.cpp create mode 100644 CTCSSDecoder.h create mode 100644 HannWindow.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp new file mode 100644 index 0000000..8905b2b --- /dev/null +++ b/CTCSSDecoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "CTCSSDecoder.h" + +CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : +m_thresholdSquared(threshold * threshold), +m_hasCTCSS(false) +{ + m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); +} + + +void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) +{ + unsigned int f1MagSquared, f2MagSquared, f3MagSquared; + GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); + + if(result == GR_READY) { + // Goertzel says it has something for us, so checkt it + // make sure the strongest tone is the wanted one and not the neighbouring tone + m_hasCTCSS = f2MagSquared >= m_thresholdSquared + && f2MagSquared > f1MagSquared + && f2MagSquared > f3MagSquared; + } +} + +bool CCTCSSDEcoder::hasCTCSS() +{ + return m_hasCTCSS; +} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h new file mode 100644 index 0000000..cf9e5e4 --- /dev/null +++ b/CTCSSDecoder.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(CTCSSDECODER_H) +#define CTCSSDECODER_H + +#include "Globals.h" +#include "Goertzel.h" +#include "HannWindow.h" + +#define N_SAMPLES 12000 + +typedef struct +{ + TGoertzelParameters toneLow, tone, toneHi; +} TCTCSSTone; + + +/* + * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. + * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. + * We need in since intermediate values in goertzel will overflow Q15 + */ +const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; +const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; +const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; +const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; +const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; +const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; +const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; +const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; +const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; +const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; +const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; +const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; +const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; +const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; +const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; +const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; +const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; +const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; +const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; +const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; +const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; +const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; +const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; +const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; +const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; +const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; +const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; +const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; +const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; +const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; +const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; +const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; +const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; +const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; +const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; +const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; +const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; +const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; +const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; +const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; +const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; +const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; +const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; +const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; +const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; +const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; +const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; +const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; +const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; +const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; +const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; +const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; +const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; +const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; +const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; + + + +class CCTCSSDEcoder { +public: + //threshold must be 0.0 to 1.0; + CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); + + void samples(const q15_t* samples, uint8_t length); + bool hasCTCSS(); + +private: + unsigned int m_thresholdSquared; + bool m_hasCTCSS; + CGoertzel* m_goertzel; + +}; + +#endif diff --git a/HannWindow.h b/HannWindow.h new file mode 100644 index 0000000..4df9a5e --- /dev/null +++ b/HannWindow.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(HANNWINDOW_H) +#define HANNWINDOW_H + +/* Hann window value as Q15 yet stored into int to avoid overflowing during calculation */ + +const int HANN_WINDOW_CORR = 16383; + +const int HANN_WINDOW[12000] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22, +22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,39,39,40,40,41,42,42,43,43,44,45,45,46,47,47,48,49,49,50, +51,51,52,53,53,54,55,55,56,57,57,58,59,60,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,74,74,75,76,77,78,78,79,80,81,82,83,84,84,85,86,87,88,89, +90,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139, +140,141,142,144,145,146,147,148,149,150,152,153,154,155,156,157,159,160,161,162,163,165,166,167,168,170,171,172,173,175,176,177,178,180,181,182,183,185,186,187,189,190,191,192,194,195,196,198,199,200, +202,203,204,206,207,209,210,211,213,214,215,217,218,220,221,222,224,225,227,228,229,231,232,234,235,237,238,240,241,243,244,245,247,248,250,251,253,254,256,257,259,261,262,264,265,267,268,270,271,273, +274,276,278,279,281,282,284,285,287,289,290,292,293,295,297,298,300,302,303,305,307,308,310,312,313,315,317,318,320,322,323,325,327,328,330,332,334,335,337,339,340,342,344,346,347,349,351,353,355,356, +358,360,362,363,365,367,369,371,373,374,376,378,380,382,383,385,387,389,391,393,395,397,398,400,402,404,406,408,410,412,414,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451, +453,455,457,459,461,463,465,467,469,471,473,475,477,479,481,483,485,487,490,492,494,496,498,500,502,504,506,508,511,513,515,517,519,521,523,526,528,530,532,534,536,539,541,543,545,547,550,552,554,556, +558,561,563,565,567,570,572,574,576,579,581,583,585,588,590,592,594,597,599,601,604,606,608,611,613,615,618,620,622,625,627,629,632,634,636,639,641,643,646,648,651,653,655,658,660,663,665,668,670,672, +675,677,680,682,685,687,690,692,694,697,699,702,704,707,709,712,714,717,719,722,724,727,729,732,735,737,740,742,745,747,750,752,755,758,760,763,765,768,771,773,776,778,781,784,786,789,791,794,797,799, +802,805,807,810,813,815,818,821,823,826,829,831,834,837,840,842,845,848,850,853,856,859,861,864,867,870,872,875,878,881,883,886,889,892,895,897,900,903,906,909,911,914,917,920,923,926,928,931,934,937, +940,943,946,949,951,954,957,960,963,966,969,972,975,978,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085, +1088,1091,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1126,1129,1132,1135,1138,1141,1144,1148,1151,1154,1157,1160,1163,1167,1170,1173,1176,1179,1183,1186,1189,1192,1195,1199,1202,1205,1208,1211,1215,1218,1221,1224,1228,1231,1234,1238,1241,1244, +1247,1251,1254,1257,1261,1264,1267,1270,1274,1277,1280,1284,1287,1290,1294,1297,1300,1304,1307,1310,1314,1317,1321,1324,1327,1331,1334,1338,1341,1344,1348,1351,1355,1358,1361,1365,1368,1372,1375,1379,1382,1385,1389,1392,1396,1399,1403,1406,1410,1413, +1417,1420,1424,1427,1431,1434,1438,1441,1445,1448,1452,1455,1459,1462,1466,1470,1473,1477,1480,1484,1487,1491,1494,1498,1502,1505,1509,1512,1516,1520,1523,1527,1530,1534,1538,1541,1545,1549,1552,1556,1560,1563,1567,1571,1574,1578,1582,1585,1589,1593, +1596,1600,1604,1607,1611,1615,1619,1622,1626,1630,1633,1637,1641,1645,1648,1652,1656,1660,1663,1667,1671,1675,1679,1682,1686,1690,1694,1698,1701,1705,1709,1713,1717,1720,1724,1728,1732,1736,1740,1743,1747,1751,1755,1759,1763,1767,1770,1774,1778,1782, +1786,1790,1794,1798,1802,1806,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893,1897,1901,1905,1909,1913,1917,1921,1925,1929,1933,1937,1941,1945,1949,1953,1957,1961,1965,1969,1974,1978,1982, +1986,1990,1994,1998,2002,2006,2010,2015,2019,2023,2027,2031,2035,2039,2043,2048,2052,2056,2060,2064,2068,2073,2077,2081,2085,2089,2094,2098,2102,2106,2110,2115,2119,2123,2127,2131,2136,2140,2144,2148,2153,2157,2161,2165,2170,2174,2178,2183,2187,2191, +2195,2200,2204,2208,2213,2217,2221,2226,2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2273,2278,2282,2286,2291,2295,2299,2304,2308,2313,2317,2321,2326,2330,2335,2339,2343,2348,2352,2357,2361,2366,2370,2375,2379,2383,2388,2392,2397,2401,2406,2410, +2415,2419,2424,2428,2433,2437,2442,2446,2451,2455,2460,2464,2469,2473,2478,2482,2487,2492,2496,2501,2505,2510,2514,2519,2523,2528,2533,2537,2542,2546,2551,2556,2560,2565,2569,2574,2579,2583,2588,2592,2597,2602,2606,2611,2616,2620,2625,2630,2634,2639, +2644,2648,2653,2658,2662,2667,2672,2676,2681,2686,2691,2695,2700,2705,2709,2714,2719,2724,2728,2733,2738,2743,2747,2752,2757,2762,2766,2771,2776,2781,2786,2790,2795,2800,2805,2810,2814,2819,2824,2829,2834,2838,2843,2848,2853,2858,2863,2867,2872,2877, +2882,2887,2892,2897,2901,2906,2911,2916,2921,2926,2931,2936,2941,2945,2950,2955,2960,2965,2970,2975,2980,2985,2990,2995,3000,3005,3010,3015,3020,3024,3029,3034,3039,3044,3049,3054,3059,3064,3069,3074,3079,3084,3089,3094,3099,3104,3109,3114,3119,3125, +3130,3135,3140,3145,3150,3155,3160,3165,3170,3175,3180,3185,3190,3195,3201,3206,3211,3216,3221,3226,3231,3236,3241,3247,3252,3257,3262,3267,3272,3277,3282,3288,3293,3298,3303,3308,3313,3319,3324,3329,3334,3339,3345,3350,3355,3360,3365,3371,3376,3381, +3386,3391,3397,3402,3407,3412,3418,3423,3428,3433,3439,3444,3449,3454,3460,3465,3470,3476,3481,3486,3491,3497,3502,3507,3513,3518,3523,3529,3534,3539,3545,3550,3555,3561,3566,3571,3577,3582,3587,3593,3598,3603,3609,3614,3619,3625,3630,3636,3641,3646, +3652,3657,3663,3668,3673,3679,3684,3690,3695,3701,3706,3711,3717,3722,3728,3733,3739,3744,3750,3755,3761,3766,3771,3777,3782,3788,3793,3799,3804,3810,3815,3821,3826,3832,3837,3843,3848,3854,3860,3865,3871,3876,3882,3887,3893,3898,3904,3909,3915,3921, +3926,3932,3937,3943,3948,3954,3960,3965,3971,3976,3982,3988,3993,3999,4004,4010,4016,4021,4027,4033,4038,4044,4050,4055,4061,4067,4072,4078,4083,4089,4095,4101,4106,4112,4118,4123,4129,4135,4140,4146,4152,4157,4163,4169,4175,4180,4186,4192,4198,4203, +4209,4215,4220,4226,4232,4238,4243,4249,4255,4261,4267,4272,4278,4284,4290,4295,4301,4307,4313,4319,4324,4330,4336,4342,4348,4354,4359,4365,4371,4377,4383,4389,4394,4400,4406,4412,4418,4424,4430,4435,4441,4447,4453,4459,4465,4471,4477,4482,4488,4494, +4500,4506,4512,4518,4524,4530,4536,4542,4548,4553,4559,4565,4571,4577,4583,4589,4595,4601,4607,4613,4619,4625,4631,4637,4643,4649,4655,4661,4667,4673,4679,4685,4691,4697,4703,4709,4715,4721,4727,4733,4739,4745,4751,4757,4763,4769,4775,4781,4787,4793, +4800,4806,4812,4818,4824,4830,4836,4842,4848,4854,4860,4866,4873,4879,4885,4891,4897,4903,4909,4915,4921,4928,4934,4940,4946,4952,4958,4964,4971,4977,4983,4989,4995,5001,5008,5014,5020,5026,5032,5039,5045,5051,5057,5063,5070,5076,5082,5088,5094,5101, +5107,5113,5119,5125,5132,5138,5144,5150,5157,5163,5169,5175,5182,5188,5194,5201,5207,5213,5219,5226,5232,5238,5244,5251,5257,5263,5270,5276,5282,5289,5295,5301,5308,5314,5320,5327,5333,5339,5346,5352,5358,5365,5371,5377,5384,5390,5396,5403,5409,5415, +5422,5428,5435,5441,5447,5454,5460,5467,5473,5479,5486,5492,5499,5505,5511,5518,5524,5531,5537,5544,5550,5556,5563,5569,5576,5582,5589,5595,5602,5608,5614,5621,5627,5634,5640,5647,5653,5660,5666,5673,5679,5686,5692,5699,5705,5712,5718,5725,5731,5738, +5744,5751,5757,5764,5770,5777,5784,5790,5797,5803,5810,5816,5823,5829,5836,5843,5849,5856,5862,5869,5875,5882,5889,5895,5902,5908,5915,5922,5928,5935,5941,5948,5955,5961,5968,5974,5981,5988,5994,6001,6008,6014,6021,6028,6034,6041,6048,6054,6061,6067, +6074,6081,6088,6094,6101,6108,6114,6121,6128,6134,6141,6148,6154,6161,6168,6174,6181,6188,6195,6201,6208,6215,6222,6228,6235,6242,6248,6255,6262,6269,6275,6282,6289,6296,6302,6309,6316,6323,6330,6336,6343,6350,6357,6363,6370,6377,6384,6391,6397,6404, +6411,6418,6425,6431,6438,6445,6452,6459,6466,6472,6479,6486,6493,6500,6507,6513,6520,6527,6534,6541,6548,6555,6561,6568,6575,6582,6589,6596,6603,6610,6616,6623,6630,6637,6644,6651,6658,6665,6672,6679,6685,6692,6699,6706,6713,6720,6727,6734,6741,6748, +6755,6762,6769,6776,6783,6790,6796,6803,6810,6817,6824,6831,6838,6845,6852,6859,6866,6873,6880,6887,6894,6901,6908,6915,6922,6929,6936,6943,6950,6957,6964,6971,6978,6985,6992,6999,7006,7013,7020,7027,7035,7042,7049,7056,7063,7070,7077,7084,7091,7098, +7105,7112,7119,7126,7133,7140,7148,7155,7162,7169,7176,7183,7190,7197,7204,7211,7219,7226,7233,7240,7247,7254,7261,7268,7276,7283,7290,7297,7304,7311,7318,7326,7333,7340,7347,7354,7361,7368,7376,7383,7390,7397,7404,7411,7419,7426,7433,7440,7447,7455, +7462,7469,7476,7483,7491,7498,7505,7512,7519,7527,7534,7541,7548,7556,7563,7570,7577,7584,7592,7599,7606,7613,7621,7628,7635,7642,7650,7657,7664,7671,7679,7686,7693,7701,7708,7715,7722,7730,7737,7744,7752,7759,7766,7773,7781,7788,7795,7803,7810,7817, +7825,7832,7839,7847,7854,7861,7869,7876,7883,7891,7898,7905,7913,7920,7927,7935,7942,7949,7957,7964,7971,7979,7986,7993,8001,8008,8016,8023,8030,8038,8045,8052,8060,8067,8075,8082,8089,8097,8104,8112,8119,8126,8134,8141,8149,8156,8164,8171,8178,8186, +8193,8201,8208,8216,8223,8230,8238,8245,8253,8260,8268,8275,8283,8290,8297,8305,8312,8320,8327,8335,8342,8350,8357,8365,8372,8380,8387,8395,8402,8410,8417,8425,8432,8440,8447,8455,8462,8470,8477,8485,8492,8500,8507,8515,8522,8530,8537,8545,8552,8560, +8568,8575,8583,8590,8598,8605,8613,8620,8628,8635,8643,8651,8658,8666,8673,8681,8688,8696,8704,8711,8719,8726,8734,8742,8749,8757,8764,8772,8779,8787,8795,8802,8810,8817,8825,8833,8840,8848,8856,8863,8871,8878,8886,8894,8901,8909,8917,8924,8932,8940, +8947,8955,8962,8970,8978,8985,8993,9001,9008,9016,9024,9031,9039,9047,9054,9062,9070,9077,9085,9093,9100,9108,9116,9124,9131,9139,9147,9154,9162,9170,9177,9185,9193,9201,9208,9216,9224,9231,9239,9247,9255,9262,9270,9278,9285,9293,9301,9309,9316,9324, +9332,9340,9347,9355,9363,9371,9378,9386,9394,9402,9409,9417,9425,9433,9440,9448,9456,9464,9472,9479,9487,9495,9503,9511,9518,9526,9534,9542,9549,9557,9565,9573,9581,9588,9596,9604,9612,9620,9628,9635,9643,9651,9659,9667,9674,9682,9690,9698,9706,9714, +9721,9729,9737,9745,9753,9761,9769,9776,9784,9792,9800,9808,9816,9824,9831,9839,9847,9855,9863,9871,9879,9886,9894,9902,9910,9918,9926,9934,9942,9950,9957,9965,9973,9981,9989,9997,10005,10013,10021,10029,10036,10044,10052,10060,10068,10076,10084,10092,10100,10108, +10116,10124,10131,10139,10147,10155,10163,10171,10179,10187,10195,10203,10211,10219,10227,10235,10243,10251,10259,10267,10274,10282,10290,10298,10306,10314,10322,10330,10338,10346,10354,10362,10370,10378,10386,10394,10402,10410,10418,10426,10434,10442,10450,10458,10466,10474,10482,10490,10498,10506, +10514,10522,10530,10538,10546,10554,10562,10570,10578,10586,10594,10602,10610,10618,10626,10634,10642,10650,10658,10667,10675,10683,10691,10699,10707,10715,10723,10731,10739,10747,10755,10763,10771,10779,10787,10795,10803,10811,10820,10828,10836,10844,10852,10860,10868,10876,10884,10892,10900,10908, +10916,10925,10933,10941,10949,10957,10965,10973,10981,10989,10997,11006,11014,11022,11030,11038,11046,11054,11062,11070,11079,11087,11095,11103,11111,11119,11127,11135,11144,11152,11160,11168,11176,11184,11192,11200,11209,11217,11225,11233,11241,11249,11257,11266,11274,11282,11290,11298,11306,11315, +11323,11331,11339,11347,11355,11364,11372,11380,11388,11396,11404,11413,11421,11429,11437,11445,11453,11462,11470,11478,11486,11494,11503,11511,11519,11527,11535,11544,11552,11560,11568,11576,11585,11593,11601,11609,11617,11626,11634,11642,11650,11658,11667,11675,11683,11691,11699,11708,11716,11724, +11732,11741,11749,11757,11765,11774,11782,11790,11798,11806,11815,11823,11831,11839,11848,11856,11864,11872,11881,11889,11897,11905,11914,11922,11930,11938,11947,11955,11963,11971,11980,11988,11996,12005,12013,12021,12029,12038,12046,12054,12062,12071,12079,12087,12096,12104,12112,12120,12129,12137, +12145,12154,12162,12170,12178,12187,12195,12203,12212,12220,12228,12236,12245,12253,12261,12270,12278,12286,12295,12303,12311,12320,12328,12336,12344,12353,12361,12369,12378,12386,12394,12403,12411,12419,12428,12436,12444,12453,12461,12469,12478,12486,12494,12503,12511,12519,12528,12536,12544,12553, +12561,12569,12578,12586,12594,12603,12611,12619,12628,12636,12644,12653,12661,12670,12678,12686,12695,12703,12711,12720,12728,12736,12745,12753,12762,12770,12778,12787,12795,12803,12812,12820,12828,12837,12845,12854,12862,12870,12879,12887,12896,12904,12912,12921,12929,12937,12946,12954,12963,12971, +12979,12988,12996,13005,13013,13021,13030,13038,13047,13055,13063,13072,13080,13089,13097,13105,13114,13122,13131,13139,13147,13156,13164,13173,13181,13189,13198,13206,13215,13223,13232,13240,13248,13257,13265,13274,13282,13291,13299,13307,13316,13324,13333,13341,13350,13358,13366,13375,13383,13392, +13400,13409,13417,13425,13434,13442,13451,13459,13468,13476,13485,13493,13501,13510,13518,13527,13535,13544,13552,13561,13569,13577,13586,13594,13603,13611,13620,13628,13637,13645,13654,13662,13670,13679,13687,13696,13704,13713,13721,13730,13738,13747,13755,13764,13772,13781,13789,13797,13806,13814, +13823,13831,13840,13848,13857,13865,13874,13882,13891,13899,13908,13916,13925,13933,13942,13950,13959,13967,13976,13984,13992,14001,14009,14018,14026,14035,14043,14052,14060,14069,14077,14086,14094,14103,14111,14120,14128,14137,14145,14154,14162,14171,14179,14188,14196,14205,14213,14222,14230,14239, +14247,14256,14264,14273,14281,14290,14298,14307,14315,14324,14332,14341,14350,14358,14367,14375,14384,14392,14401,14409,14418,14426,14435,14443,14452,14460,14469,14477,14486,14494,14503,14511,14520,14528,14537,14545,14554,14563,14571,14580,14588,14597,14605,14614,14622,14631,14639,14648,14656,14665, +14673,14682,14690,14699,14708,14716,14725,14733,14742,14750,14759,14767,14776,14784,14793,14801,14810,14819,14827,14836,14844,14853,14861,14870,14878,14887,14895,14904,14912,14921,14930,14938,14947,14955,14964,14972,14981,14989,14998,15006,15015,15024,15032,15041,15049,15058,15066,15075,15083,15092, +15101,15109,15118,15126,15135,15143,15152,15160,15169,15178,15186,15195,15203,15212,15220,15229,15237,15246,15255,15263,15272,15280,15289,15297,15306,15314,15323,15332,15340,15349,15357,15366,15374,15383,15392,15400,15409,15417,15426,15434,15443,15451,15460,15469,15477,15486,15494,15503,15511,15520, +15529,15537,15546,15554,15563,15571,15580,15589,15597,15606,15614,15623,15631,15640,15649,15657,15666,15674,15683,15691,15700,15709,15717,15726,15734,15743,15751,15760,15769,15777,15786,15794,15803,15811,15820,15829,15837,15846,15854,15863,15871,15880,15889,15897,15906,15914,15923,15931,15940,15949, +15957,15966,15974,15983,15992,16000,16009,16017,16026,16034,16043,16052,16060,16069,16077,16086,16094,16103,16112,16120,16129,16137,16146,16155,16163,16172,16180,16189,16197,16206,16215,16223,16232,16240,16249,16257,16266,16275,16283,16292,16300,16309,16318,16326,16335,16343,16352,16360,16369,16378, +16386,16395,16403,16412,16420,16429,16438,16446,16455,16463,16472,16481,16489,16498,16506,16515,16523,16532,16541,16549,16558,16566,16575,16583,16592,16601,16609,16618,16626,16635,16644,16652,16661,16669,16678,16686,16695,16704,16712,16721,16729,16738,16746,16755,16764,16772,16781,16789,16798,16806, +16815,16824,16832,16841,16849,16858,16867,16875,16884,16892,16901,16909,16918,16927,16935,16944,16952,16961,16969,16978,16987,16995,17004,17012,17021,17029,17038,17047,17055,17064,17072,17081,17089,17098,17107,17115,17124,17132,17141,17149,17158,17167,17175,17184,17192,17201,17209,17218,17227,17235, +17244,17252,17261,17269,17278,17287,17295,17304,17312,17321,17329,17338,17346,17355,17364,17372,17381,17389,17398,17406,17415,17424,17432,17441,17449,17458,17466,17475,17483,17492,17501,17509,17518,17526,17535,17543,17552,17561,17569,17578,17586,17595,17603,17612,17620,17629,17638,17646,17655,17663, +17672,17680,17689,17697,17706,17714,17723,17732,17740,17749,17757,17766,17774,17783,17791,17800,17809,17817,17826,17834,17843,17851,17860,17868,17877,17885,17894,17903,17911,17920,17928,17937,17945,17954,17962,17971,17979,17988,17996,18005,18014,18022,18031,18039,18048,18056,18065,18073,18082,18090, +18099,18107,18116,18124,18133,18142,18150,18159,18167,18176,18184,18193,18201,18210,18218,18227,18235,18244,18252,18261,18269,18278,18286,18295,18304,18312,18321,18329,18338,18346,18355,18363,18372,18380,18389,18397,18406,18414,18423,18431,18440,18448,18457,18465,18474,18482,18491,18499,18508,18516, +18525,18533,18542,18550,18559,18567,18576,18584,18593,18601,18610,18618,18627,18635,18644,18652,18661,18669,18678,18686,18695,18703,18712,18720,18729,18737,18746,18754,18763,18771,18780,18788,18797,18805,18814,18822,18831,18839,18848,18856,18865,18873,18882,18890,18898,18907,18915,18924,18932,18941, +18949,18958,18966,18975,18983,18992,19000,19009,19017,19026,19034,19043,19051,19059,19068,19076,19085,19093,19102,19110,19119,19127,19136,19144,19153,19161,19169,19178,19186,19195,19203,19212,19220,19229,19237,19245,19254,19262,19271,19279,19288,19296,19305,19313,19321,19330,19338,19347,19355,19364, +19372,19381,19389,19397,19406,19414,19423,19431,19440,19448,19456,19465,19473,19482,19490,19499,19507,19515,19524,19532,19541,19549,19557,19566,19574,19583,19591,19600,19608,19616,19625,19633,19642,19650,19658,19667,19675,19684,19692,19700,19709,19717,19726,19734,19742,19751,19759,19768,19776,19784, +19793,19801,19810,19818,19826,19835,19843,19852,19860,19868,19877,19885,19893,19902,19910,19919,19927,19935,19944,19952,19960,19969,19977,19986,19994,20002,20011,20019,20027,20036,20044,20052,20061,20069,20078,20086,20094,20103,20111,20119,20128,20136,20144,20153,20161,20169,20178,20186,20194,20203, +20211,20220,20228,20236,20245,20253,20261,20270,20278,20286,20295,20303,20311,20320,20328,20336,20345,20353,20361,20369,20378,20386,20394,20403,20411,20419,20428,20436,20444,20453,20461,20469,20478,20486,20494,20502,20511,20519,20527,20536,20544,20552,20561,20569,20577,20585,20594,20602,20610,20619, +20627,20635,20643,20652,20660,20668,20677,20685,20693,20701,20710,20718,20726,20735,20743,20751,20759,20768,20776,20784,20792,20801,20809,20817,20825,20834,20842,20850,20858,20867,20875,20883,20891,20900,20908,20916,20924,20933,20941,20949,20957,20966,20974,20982,20990,20999,21007,21015,21023,21032, +21040,21048,21056,21064,21073,21081,21089,21097,21106,21114,21122,21130,21138,21147,21155,21163,21171,21179,21188,21196,21204,21212,21220,21229,21237,21245,21253,21261,21270,21278,21286,21294,21302,21310,21319,21327,21335,21343,21351,21360,21368,21376,21384,21392,21400,21409,21417,21425,21433,21441, +21449,21458,21466,21474,21482,21490,21498,21506,21515,21523,21531,21539,21547,21555,21563,21572,21580,21588,21596,21604,21612,21620,21629,21637,21645,21653,21661,21669,21677,21685,21694,21702,21710,21718,21726,21734,21742,21750,21758,21767,21775,21783,21791,21799,21807,21815,21823,21831,21839,21847, +21856,21864,21872,21880,21888,21896,21904,21912,21920,21928,21936,21944,21952,21961,21969,21977,21985,21993,22001,22009,22017,22025,22033,22041,22049,22057,22065,22073,22081,22089,22097,22106,22114,22122,22130,22138,22146,22154,22162,22170,22178,22186,22194,22202,22210,22218,22226,22234,22242,22250, +22258,22266,22274,22282,22290,22298,22306,22314,22322,22330,22338,22346,22354,22362,22370,22378,22386,22394,22402,22410,22418,22426,22434,22442,22450,22458,22466,22474,22482,22490,22498,22505,22513,22521,22529,22537,22545,22553,22561,22569,22577,22585,22593,22601,22609,22617,22625,22633,22641,22648, +22656,22664,22672,22680,22688,22696,22704,22712,22720,22728,22736,22743,22751,22759,22767,22775,22783,22791,22799,22807,22815,22822,22830,22838,22846,22854,22862,22870,22878,22885,22893,22901,22909,22917,22925,22933,22941,22948,22956,22964,22972,22980,22988,22996,23003,23011,23019,23027,23035,23043, +23050,23058,23066,23074,23082,23090,23097,23105,23113,23121,23129,23137,23144,23152,23160,23168,23176,23183,23191,23199,23207,23215,23222,23230,23238,23246,23254,23261,23269,23277,23285,23293,23300,23308,23316,23324,23331,23339,23347,23355,23362,23370,23378,23386,23393,23401,23409,23417,23424,23432, +23440,23448,23455,23463,23471,23479,23486,23494,23502,23510,23517,23525,23533,23540,23548,23556,23564,23571,23579,23587,23594,23602,23610,23618,23625,23633,23641,23648,23656,23664,23671,23679,23687,23694,23702,23710,23717,23725,23733,23740,23748,23756,23763,23771,23779,23786,23794,23802,23809,23817, +23825,23832,23840,23848,23855,23863,23870,23878,23886,23893,23901,23909,23916,23924,23931,23939,23947,23954,23962,23970,23977,23985,23992,24000,24008,24015,24023,24030,24038,24045,24053,24061,24068,24076,24083,24091,24098,24106,24114,24121,24129,24136,24144,24151,24159,24167,24174,24182,24189,24197, +24204,24212,24219,24227,24234,24242,24249,24257,24264,24272,24280,24287,24295,24302,24310,24317,24325,24332,24340,24347,24355,24362,24370,24377,24385,24392,24400,24407,24414,24422,24429,24437,24444,24452,24459,24467,24474,24482,24489,24497,24504,24512,24519,24526,24534,24541,24549,24556,24564,24571, +24578,24586,24593,24601,24608,24616,24623,24630,24638,24645,24653,24660,24667,24675,24682,24690,24697,24704,24712,24719,24727,24734,24741,24749,24756,24763,24771,24778,24786,24793,24800,24808,24815,24822,24830,24837,24844,24852,24859,24866,24874,24881,24888,24896,24903,24910,24918,24925,24932,24940, +24947,24954,24962,24969,24976,24984,24991,24998,25006,25013,25020,25027,25035,25042,25049,25057,25064,25071,25078,25086,25093,25100,25107,25115,25122,25129,25136,25144,25151,25158,25165,25173,25180,25187,25194,25202,25209,25216,25223,25231,25238,25245,25252,25259,25267,25274,25281,25288,25295,25303, +25310,25317,25324,25331,25339,25346,25353,25360,25367,25374,25382,25389,25396,25403,25410,25417,25425,25432,25439,25446,25453,25460,25468,25475,25482,25489,25496,25503,25510,25517,25525,25532,25539,25546,25553,25560,25567,25574,25581,25589,25596,25603,25610,25617,25624,25631,25638,25645,25652,25659, +25666,25674,25681,25688,25695,25702,25709,25716,25723,25730,25737,25744,25751,25758,25765,25772,25779,25786,25793,25800,25807,25814,25821,25828,25835,25842,25849,25856,25863,25870,25877,25884,25891,25898,25905,25912,25919,25926,25933,25940,25947,25954,25961,25968,25975,25982,25989,25996,26003,26010, +26017,26024,26031,26038,26044,26051,26058,26065,26072,26079,26086,26093,26100,26107,26114,26121,26127,26134,26141,26148,26155,26162,26169,26176,26182,26189,26196,26203,26210,26217,26224,26231,26237,26244,26251,26258,26265,26272,26278,26285,26292,26299,26306,26313,26319,26326,26333,26340,26347,26354, +26360,26367,26374,26381,26388,26394,26401,26408,26415,26421,26428,26435,26442,26449,26455,26462,26469,26476,26482,26489,26496,26503,26509,26516,26523,26530,26536,26543,26550,26557,26563,26570,26577,26583,26590,26597,26604,26610,26617,26624,26630,26637,26644,26650,26657,26664,26670,26677,26684,26691, +26697,26704,26711,26717,26724,26730,26737,26744,26750,26757,26764,26770,26777,26784,26790,26797,26803,26810,26817,26823,26830,26837,26843,26850,26856,26863,26870,26876,26883,26889,26896,26902,26909,26916,26922,26929,26935,26942,26948,26955,26962,26968,26975,26981,26988,26994,27001,27007,27014,27020, +27027,27033,27040,27046,27053,27059,27066,27073,27079,27086,27092,27098,27105,27111,27118,27124,27131,27137,27144,27150,27157,27163,27170,27176,27183,27189,27196,27202,27208,27215,27221,27228,27234,27241,27247,27253,27260,27266,27273,27279,27285,27292,27298,27305,27311,27317,27324,27330,27337,27343, +27349,27356,27362,27368,27375,27381,27388,27394,27400,27407,27413,27419,27426,27432,27438,27445,27451,27457,27464,27470,27476,27483,27489,27495,27501,27508,27514,27520,27527,27533,27539,27546,27552,27558,27564,27571,27577,27583,27589,27596,27602,27608,27614,27621,27627,27633,27639,27646,27652,27658, +27664,27671,27677,27683,27689,27695,27702,27708,27714,27720,27726,27733,27739,27745,27751,27757,27763,27770,27776,27782,27788,27794,27800,27807,27813,27819,27825,27831,27837,27843,27850,27856,27862,27868,27874,27880,27886,27892,27898,27905,27911,27917,27923,27929,27935,27941,27947,27953,27959,27965, +27972,27978,27984,27990,27996,28002,28008,28014,28020,28026,28032,28038,28044,28050,28056,28062,28068,28074,28080,28086,28092,28098,28104,28110,28116,28122,28128,28134,28140,28146,28152,28158,28164,28170,28176,28182,28188,28194,28200,28206,28212,28218,28223,28229,28235,28241,28247,28253,28259,28265, +28271,28277,28283,28288,28294,28300,28306,28312,28318,28324,28330,28336,28341,28347,28353,28359,28365,28371,28377,28382,28388,28394,28400,28406,28412,28417,28423,28429,28435,28441,28446,28452,28458,28464,28470,28475,28481,28487,28493,28499,28504,28510,28516,28522,28527,28533,28539,28545,28550,28556, +28562,28568,28573,28579,28585,28591,28596,28602,28608,28613,28619,28625,28631,28636,28642,28648,28653,28659,28665,28670,28676,28682,28687,28693,28699,28704,28710,28716,28721,28727,28733,28738,28744,28749,28755,28761,28766,28772,28778,28783,28789,28794,28800,28806,28811,28817,28822,28828,28834,28839, +28845,28850,28856,28861,28867,28872,28878,28884,28889,28895,28900,28906,28911,28917,28922,28928,28933,28939,28944,28950,28955,28961,28966,28972,28977,28983,28988,28994,28999,29005,29010,29016,29021,29027,29032,29038,29043,29048,29054,29059,29065,29070,29076,29081,29086,29092,29097,29103,29108,29113, +29119,29124,29130,29135,29140,29146,29151,29157,29162,29167,29173,29178,29183,29189,29194,29199,29205,29210,29215,29221,29226,29231,29237,29242,29247,29253,29258,29263,29269,29274,29279,29285,29290,29295,29300,29306,29311,29316,29321,29327,29332,29337,29342,29348,29353,29358,29363,29369,29374,29379, +29384,29390,29395,29400,29405,29410,29416,29421,29426,29431,29436,29442,29447,29452,29457,29462,29467,29473,29478,29483,29488,29493,29498,29504,29509,29514,29519,29524,29529,29534,29539,29545,29550,29555,29560,29565,29570,29575,29580,29585,29590,29595,29601,29606,29611,29616,29621,29626,29631,29636, +29641,29646,29651,29656,29661,29666,29671,29676,29681,29686,29691,29696,29701,29706,29711,29716,29721,29726,29731,29736,29741,29746,29751,29756,29761,29766,29771,29776,29781,29786,29791,29795,29800,29805,29810,29815,29820,29825,29830,29835,29840,29845,29849,29854,29859,29864,29869,29874,29879,29884, +29888,29893,29898,29903,29908,29913,29918,29922,29927,29932,29937,29942,29946,29951,29956,29961,29966,29970,29975,29980,29985,29990,29994,29999,30004,30009,30013,30018,30023,30028,30033,30037,30042,30047,30051,30056,30061,30066,30070,30075,30080,30084,30089,30094,30099,30103,30108,30113,30117,30122, +30127,30131,30136,30141,30145,30150,30155,30159,30164,30169,30173,30178,30182,30187,30192,30196,30201,30206,30210,30215,30219,30224,30229,30233,30238,30242,30247,30251,30256,30261,30265,30270,30274,30279,30283,30288,30292,30297,30301,30306,30311,30315,30320,30324,30329,30333,30338,30342,30347,30351, +30356,30360,30364,30369,30373,30378,30382,30387,30391,30396,30400,30405,30409,30413,30418,30422,30427,30431,30436,30440,30444,30449,30453,30458,30462,30466,30471,30475,30479,30484,30488,30493,30497,30501,30506,30510,30514,30519,30523,30527,30532,30536,30540,30545,30549,30553,30558,30562,30566,30570, +30575,30579,30583,30588,30592,30596,30600,30605,30609,30613,30617,30622,30626,30630,30634,30639,30643,30647,30651,30656,30660,30664,30668,30672,30677,30681,30685,30689,30693,30697,30702,30706,30710,30714,30718,30722,30727,30731,30735,30739,30743,30747,30751,30756,30760,30764,30768,30772,30776,30780, +30784,30788,30792,30797,30801,30805,30809,30813,30817,30821,30825,30829,30833,30837,30841,30845,30849,30853,30857,30861,30865,30869,30873,30877,30881,30885,30889,30893,30897,30901,30905,30909,30913,30917,30921,30925,30929,30933,30937,30941,30945,30949,30953,30957,30960,30964,30968,30972,30976,30980, +30984,30988,30992,30996,30999,31003,31007,31011,31015,31019,31023,31026,31030,31034,31038,31042,31046,31050,31053,31057,31061,31065,31069,31072,31076,31080,31084,31088,31091,31095,31099,31103,31106,31110,31114,31118,31121,31125,31129,31133,31136,31140,31144,31148,31151,31155,31159,31162,31166,31170, +31174,31177,31181,31185,31188,31192,31196,31199,31203,31207,31210,31214,31218,31221,31225,31228,31232,31236,31239,31243,31247,31250,31254,31257,31261,31265,31268,31272,31275,31279,31282,31286,31290,31293,31297,31300,31304,31307,31311,31314,31318,31321,31325,31329,31332,31336,31339,31343,31346,31350, +31353,31357,31360,31363,31367,31370,31374,31377,31381,31384,31388,31391,31395,31398,31401,31405,31408,31412,31415,31419,31422,31425,31429,31432,31436,31439,31442,31446,31449,31452,31456,31459,31463,31466,31469,31473,31476,31479,31483,31486,31489,31493,31496,31499,31503,31506,31509,31512,31516,31519, +31522,31526,31529,31532,31535,31539,31542,31545,31548,31552,31555,31558,31561,31565,31568,31571,31574,31577,31581,31584,31587,31590,31593,31597,31600,31603,31606,31609,31613,31616,31619,31622,31625,31628,31631,31635,31638,31641,31644,31647,31650,31653,31656,31660,31663,31666,31669,31672,31675,31678, +31681,31684,31687,31690,31693,31696,31700,31703,31706,31709,31712,31715,31718,31721,31724,31727,31730,31733,31736,31739,31742,31745,31748,31751,31754,31757,31760,31763,31766,31768,31771,31774,31777,31780,31783,31786,31789,31792,31795,31798,31801,31804,31806,31809,31812,31815,31818,31821,31824,31827, +31830,31832,31835,31838,31841,31844,31847,31849,31852,31855,31858,31861,31864,31866,31869,31872,31875,31878,31880,31883,31886,31889,31891,31894,31897,31900,31902,31905,31908,31911,31913,31916,31919,31922,31924,31927,31930,31933,31935,31938,31941,31943,31946,31949,31951,31954,31957,31959,31962,31965, +31967,31970,31973,31975,31978,31980,31983,31986,31988,31991,31994,31996,31999,32001,32004,32007,32009,32012,32014,32017,32019,32022,32025,32027,32030,32032,32035,32037,32040,32042,32045,32047,32050,32052,32055,32057,32060,32062,32065,32067,32070,32072,32075,32077,32080,32082,32085,32087,32090,32092, +32094,32097,32099,32102,32104,32107,32109,32111,32114,32116,32119,32121,32123,32126,32128,32130,32133,32135,32138,32140,32142,32145,32147,32149,32152,32154,32156,32159,32161,32163,32166,32168,32170,32172,32175,32177,32179,32182,32184,32186,32188,32191,32193,32195,32197,32200,32202,32204,32206,32209, +32211,32213,32215,32217,32220,32222,32224,32226,32228,32231,32233,32235,32237,32239,32241,32244,32246,32248,32250,32252,32254,32256,32259,32261,32263,32265,32267,32269,32271,32273,32275,32277,32280,32282,32284,32286,32288,32290,32292,32294,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314, +32316,32318,32320,32322,32324,32326,32328,32330,32332,32334,32336,32338,32340,32342,32344,32346,32348,32350,32352,32353,32355,32357,32359,32361,32363,32365,32367,32369,32371,32372,32374,32376,32378,32380,32382,32384,32385,32387,32389,32391,32393,32395,32396,32398,32400,32402,32404,32405,32407,32409, +32411,32413,32414,32416,32418,32420,32421,32423,32425,32427,32428,32430,32432,32434,32435,32437,32439,32440,32442,32444,32446,32447,32449,32451,32452,32454,32456,32457,32459,32461,32462,32464,32466,32467,32469,32470,32472,32474,32475,32477,32479,32480,32482,32483,32485,32487,32488,32490,32491,32493, +32494,32496,32497,32499,32501,32502,32504,32505,32507,32508,32510,32511,32513,32514,32516,32517,32519,32520,32522,32523,32525,32526,32528,32529,32531,32532,32533,32535,32536,32538,32539,32541,32542,32544,32545,32546,32548,32549,32551,32552,32553,32555,32556,32557,32559,32560,32562,32563,32564,32566, +32567,32568,32570,32571,32572,32574,32575,32576,32578,32579,32580,32581,32583,32584,32585,32587,32588,32589,32590,32592,32593,32594,32595,32597,32598,32599,32600,32601,32603,32604,32605,32606,32608,32609,32610,32611,32612,32613,32615,32616,32617,32618,32619,32620,32622,32623,32624,32625,32626,32627, +32628,32629,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32670,32671,32672,32673,32674,32675,32676,32677,32678, +32679,32680,32680,32681,32682,32683,32684,32685,32686,32687,32687,32688,32689,32690,32691,32692,32692,32693,32694,32695,32696,32696,32697,32698,32699,32700,32700,32701,32702,32703,32704,32704,32705,32706,32707,32707,32708,32709,32709,32710,32711,32712,32712,32713,32714,32714,32715,32716,32716,32717, +32718,32718,32719,32720,32720,32721,32722,32722,32723,32724,32724,32725,32726,32726,32727,32727,32728,32729,32729,32730,32730,32731,32731,32732,32733,32733,32734,32734,32735,32735,32736,32736,32737,32738,32738,32739,32739,32740,32740,32741,32741,32742,32742,32743,32743,32743,32744,32744,32745,32745, +32746,32746,32747,32747,32748,32748,32748,32749,32749,32750,32750,32750,32751,32751,32752,32752,32752,32753,32753,32753,32754,32754,32755,32755,32755,32756,32756,32756,32757,32757,32757,32757,32758,32758,32758,32759,32759,32759,32760,32760,32760,32760,32761,32761,32761,32761,32762,32762,32762,32762, +32762,32763,32763,32763,32763,32764,32764,32764,32764,32764,32764,32765,32765,32765,32765,32765,32765,32766,32766,32766,32766,32766,32766,32766,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768, +32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32766,32766,32766,32766,32766,32766,32766,32765,32765,32765,32765,32765,32765,32764,32764,32764,32764,32764,32764,32763,32763,32763,32763,32762, +32762,32762,32762,32762,32761,32761,32761,32761,32760,32760,32760,32760,32759,32759,32759,32758,32758,32758,32757,32757,32757,32757,32756,32756,32756,32755,32755,32755,32754,32754,32753,32753,32753,32752,32752,32752,32751,32751,32750,32750,32750,32749,32749,32748,32748,32748,32747,32747,32746,32746, +32745,32745,32744,32744,32743,32743,32743,32742,32742,32741,32741,32740,32740,32739,32739,32738,32738,32737,32736,32736,32735,32735,32734,32734,32733,32733,32732,32731,32731,32730,32730,32729,32729,32728,32727,32727,32726,32726,32725,32724,32724,32723,32722,32722,32721,32720,32720,32719,32718,32718, +32717,32716,32716,32715,32714,32714,32713,32712,32712,32711,32710,32709,32709,32708,32707,32707,32706,32705,32704,32704,32703,32702,32701,32700,32700,32699,32698,32697,32696,32696,32695,32694,32693,32692,32692,32691,32690,32689,32688,32687,32687,32686,32685,32684,32683,32682,32681,32680,32680,32679, +32678,32677,32676,32675,32674,32673,32672,32671,32670,32670,32669,32668,32667,32666,32665,32664,32663,32662,32661,32660,32659,32658,32657,32656,32655,32654,32653,32652,32651,32650,32649,32648,32647,32646,32645,32644,32643,32641,32640,32639,32638,32637,32636,32635,32634,32633,32632,32631,32629,32628, +32627,32626,32625,32624,32623,32622,32620,32619,32618,32617,32616,32615,32613,32612,32611,32610,32609,32608,32606,32605,32604,32603,32601,32600,32599,32598,32597,32595,32594,32593,32592,32590,32589,32588,32587,32585,32584,32583,32581,32580,32579,32578,32576,32575,32574,32572,32571,32570,32568,32567, +32566,32564,32563,32562,32560,32559,32557,32556,32555,32553,32552,32551,32549,32548,32546,32545,32544,32542,32541,32539,32538,32536,32535,32533,32532,32531,32529,32528,32526,32525,32523,32522,32520,32519,32517,32516,32514,32513,32511,32510,32508,32507,32505,32504,32502,32501,32499,32497,32496,32494, +32493,32491,32490,32488,32487,32485,32483,32482,32480,32479,32477,32475,32474,32472,32470,32469,32467,32466,32464,32462,32461,32459,32457,32456,32454,32452,32451,32449,32447,32446,32444,32442,32440,32439,32437,32435,32434,32432,32430,32428,32427,32425,32423,32421,32420,32418,32416,32414,32413,32411, +32409,32407,32405,32404,32402,32400,32398,32396,32395,32393,32391,32389,32387,32385,32384,32382,32380,32378,32376,32374,32372,32371,32369,32367,32365,32363,32361,32359,32357,32355,32353,32352,32350,32348,32346,32344,32342,32340,32338,32336,32334,32332,32330,32328,32326,32324,32322,32320,32318,32316, +32314,32312,32310,32308,32306,32304,32302,32300,32298,32296,32294,32292,32290,32288,32286,32284,32282,32280,32277,32275,32273,32271,32269,32267,32265,32263,32261,32259,32256,32254,32252,32250,32248,32246,32244,32241,32239,32237,32235,32233,32231,32228,32226,32224,32222,32220,32217,32215,32213,32211, +32209,32206,32204,32202,32200,32197,32195,32193,32191,32188,32186,32184,32182,32179,32177,32175,32172,32170,32168,32166,32163,32161,32159,32156,32154,32152,32149,32147,32145,32142,32140,32138,32135,32133,32130,32128,32126,32123,32121,32119,32116,32114,32111,32109,32107,32104,32102,32099,32097,32094, +32092,32090,32087,32085,32082,32080,32077,32075,32072,32070,32067,32065,32062,32060,32057,32055,32052,32050,32047,32045,32042,32040,32037,32035,32032,32030,32027,32025,32022,32019,32017,32014,32012,32009,32007,32004,32001,31999,31996,31994,31991,31988,31986,31983,31980,31978,31975,31973,31970,31967, +31965,31962,31959,31957,31954,31951,31949,31946,31943,31941,31938,31935,31933,31930,31927,31924,31922,31919,31916,31913,31911,31908,31905,31902,31900,31897,31894,31891,31889,31886,31883,31880,31878,31875,31872,31869,31866,31864,31861,31858,31855,31852,31849,31847,31844,31841,31838,31835,31832,31830, +31827,31824,31821,31818,31815,31812,31809,31806,31804,31801,31798,31795,31792,31789,31786,31783,31780,31777,31774,31771,31768,31766,31763,31760,31757,31754,31751,31748,31745,31742,31739,31736,31733,31730,31727,31724,31721,31718,31715,31712,31709,31706,31703,31700,31696,31693,31690,31687,31684,31681, +31678,31675,31672,31669,31666,31663,31660,31656,31653,31650,31647,31644,31641,31638,31635,31631,31628,31625,31622,31619,31616,31613,31609,31606,31603,31600,31597,31593,31590,31587,31584,31581,31577,31574,31571,31568,31565,31561,31558,31555,31552,31548,31545,31542,31539,31535,31532,31529,31526,31522, +31519,31516,31512,31509,31506,31503,31499,31496,31493,31489,31486,31483,31479,31476,31473,31469,31466,31463,31459,31456,31452,31449,31446,31442,31439,31436,31432,31429,31425,31422,31419,31415,31412,31408,31405,31401,31398,31395,31391,31388,31384,31381,31377,31374,31370,31367,31363,31360,31357,31353, +31350,31346,31343,31339,31336,31332,31329,31325,31321,31318,31314,31311,31307,31304,31300,31297,31293,31290,31286,31282,31279,31275,31272,31268,31265,31261,31257,31254,31250,31247,31243,31239,31236,31232,31228,31225,31221,31218,31214,31210,31207,31203,31199,31196,31192,31188,31185,31181,31177,31174, +31170,31166,31162,31159,31155,31151,31148,31144,31140,31136,31133,31129,31125,31121,31118,31114,31110,31106,31103,31099,31095,31091,31088,31084,31080,31076,31072,31069,31065,31061,31057,31053,31050,31046,31042,31038,31034,31030,31026,31023,31019,31015,31011,31007,31003,30999,30996,30992,30988,30984, +30980,30976,30972,30968,30964,30960,30957,30953,30949,30945,30941,30937,30933,30929,30925,30921,30917,30913,30909,30905,30901,30897,30893,30889,30885,30881,30877,30873,30869,30865,30861,30857,30853,30849,30845,30841,30837,30833,30829,30825,30821,30817,30813,30809,30805,30801,30797,30792,30788,30784, +30780,30776,30772,30768,30764,30760,30756,30751,30747,30743,30739,30735,30731,30727,30722,30718,30714,30710,30706,30702,30697,30693,30689,30685,30681,30677,30672,30668,30664,30660,30656,30651,30647,30643,30639,30634,30630,30626,30622,30617,30613,30609,30605,30600,30596,30592,30588,30583,30579,30575, +30570,30566,30562,30558,30553,30549,30545,30540,30536,30532,30527,30523,30519,30514,30510,30506,30501,30497,30493,30488,30484,30479,30475,30471,30466,30462,30458,30453,30449,30444,30440,30436,30431,30427,30422,30418,30413,30409,30405,30400,30396,30391,30387,30382,30378,30373,30369,30364,30360,30356, +30351,30347,30342,30338,30333,30329,30324,30320,30315,30311,30306,30301,30297,30292,30288,30283,30279,30274,30270,30265,30261,30256,30251,30247,30242,30238,30233,30229,30224,30219,30215,30210,30206,30201,30196,30192,30187,30182,30178,30173,30169,30164,30159,30155,30150,30145,30141,30136,30131,30127, +30122,30117,30113,30108,30103,30099,30094,30089,30084,30080,30075,30070,30066,30061,30056,30051,30047,30042,30037,30033,30028,30023,30018,30013,30009,30004,29999,29994,29990,29985,29980,29975,29970,29966,29961,29956,29951,29946,29942,29937,29932,29927,29922,29918,29913,29908,29903,29898,29893,29888, +29884,29879,29874,29869,29864,29859,29854,29849,29845,29840,29835,29830,29825,29820,29815,29810,29805,29800,29795,29791,29786,29781,29776,29771,29766,29761,29756,29751,29746,29741,29736,29731,29726,29721,29716,29711,29706,29701,29696,29691,29686,29681,29676,29671,29666,29661,29656,29651,29646,29641, +29636,29631,29626,29621,29616,29611,29606,29601,29595,29590,29585,29580,29575,29570,29565,29560,29555,29550,29545,29539,29534,29529,29524,29519,29514,29509,29504,29498,29493,29488,29483,29478,29473,29467,29462,29457,29452,29447,29442,29436,29431,29426,29421,29416,29410,29405,29400,29395,29390,29384, +29379,29374,29369,29363,29358,29353,29348,29342,29337,29332,29327,29321,29316,29311,29306,29300,29295,29290,29285,29279,29274,29269,29263,29258,29253,29247,29242,29237,29231,29226,29221,29215,29210,29205,29199,29194,29189,29183,29178,29173,29167,29162,29157,29151,29146,29140,29135,29130,29124,29119, +29113,29108,29103,29097,29092,29086,29081,29076,29070,29065,29059,29054,29048,29043,29038,29032,29027,29021,29016,29010,29005,28999,28994,28988,28983,28977,28972,28966,28961,28955,28950,28944,28939,28933,28928,28922,28917,28911,28906,28900,28895,28889,28884,28878,28872,28867,28861,28856,28850,28845, +28839,28834,28828,28822,28817,28811,28806,28800,28794,28789,28783,28778,28772,28766,28761,28755,28749,28744,28738,28733,28727,28721,28716,28710,28704,28699,28693,28687,28682,28676,28670,28665,28659,28653,28648,28642,28636,28631,28625,28619,28613,28608,28602,28596,28591,28585,28579,28573,28568,28562, +28556,28550,28545,28539,28533,28527,28522,28516,28510,28504,28499,28493,28487,28481,28475,28470,28464,28458,28452,28446,28441,28435,28429,28423,28417,28412,28406,28400,28394,28388,28382,28377,28371,28365,28359,28353,28347,28341,28336,28330,28324,28318,28312,28306,28300,28294,28288,28283,28277,28271, +28265,28259,28253,28247,28241,28235,28229,28223,28218,28212,28206,28200,28194,28188,28182,28176,28170,28164,28158,28152,28146,28140,28134,28128,28122,28116,28110,28104,28098,28092,28086,28080,28074,28068,28062,28056,28050,28044,28038,28032,28026,28020,28014,28008,28002,27996,27990,27984,27978,27972, +27965,27959,27953,27947,27941,27935,27929,27923,27917,27911,27905,27898,27892,27886,27880,27874,27868,27862,27856,27850,27843,27837,27831,27825,27819,27813,27807,27800,27794,27788,27782,27776,27770,27763,27757,27751,27745,27739,27733,27726,27720,27714,27708,27702,27695,27689,27683,27677,27671,27664, +27658,27652,27646,27639,27633,27627,27621,27614,27608,27602,27596,27589,27583,27577,27571,27564,27558,27552,27546,27539,27533,27527,27520,27514,27508,27501,27495,27489,27483,27476,27470,27464,27457,27451,27445,27438,27432,27426,27419,27413,27407,27400,27394,27388,27381,27375,27368,27362,27356,27349, +27343,27337,27330,27324,27317,27311,27305,27298,27292,27285,27279,27273,27266,27260,27253,27247,27241,27234,27228,27221,27215,27208,27202,27196,27189,27183,27176,27170,27163,27157,27150,27144,27137,27131,27124,27118,27111,27105,27098,27092,27086,27079,27073,27066,27059,27053,27046,27040,27033,27027, +27020,27014,27007,27001,26994,26988,26981,26975,26968,26962,26955,26948,26942,26935,26929,26922,26916,26909,26902,26896,26889,26883,26876,26870,26863,26856,26850,26843,26837,26830,26823,26817,26810,26803,26797,26790,26784,26777,26770,26764,26757,26750,26744,26737,26730,26724,26717,26711,26704,26697, +26691,26684,26677,26670,26664,26657,26650,26644,26637,26630,26624,26617,26610,26604,26597,26590,26583,26577,26570,26563,26557,26550,26543,26536,26530,26523,26516,26509,26503,26496,26489,26482,26476,26469,26462,26455,26449,26442,26435,26428,26421,26415,26408,26401,26394,26388,26381,26374,26367,26360, +26354,26347,26340,26333,26326,26319,26313,26306,26299,26292,26285,26278,26272,26265,26258,26251,26244,26237,26231,26224,26217,26210,26203,26196,26189,26182,26176,26169,26162,26155,26148,26141,26134,26127,26121,26114,26107,26100,26093,26086,26079,26072,26065,26058,26051,26044,26038,26031,26024,26017, +26010,26003,25996,25989,25982,25975,25968,25961,25954,25947,25940,25933,25926,25919,25912,25905,25898,25891,25884,25877,25870,25863,25856,25849,25842,25835,25828,25821,25814,25807,25800,25793,25786,25779,25772,25765,25758,25751,25744,25737,25730,25723,25716,25709,25702,25695,25688,25681,25674,25666, +25659,25652,25645,25638,25631,25624,25617,25610,25603,25596,25589,25581,25574,25567,25560,25553,25546,25539,25532,25525,25517,25510,25503,25496,25489,25482,25475,25468,25460,25453,25446,25439,25432,25425,25417,25410,25403,25396,25389,25382,25374,25367,25360,25353,25346,25339,25331,25324,25317,25310, +25303,25295,25288,25281,25274,25267,25259,25252,25245,25238,25231,25223,25216,25209,25202,25194,25187,25180,25173,25165,25158,25151,25144,25136,25129,25122,25115,25107,25100,25093,25086,25078,25071,25064,25057,25049,25042,25035,25027,25020,25013,25006,24998,24991,24984,24976,24969,24962,24954,24947, +24940,24932,24925,24918,24910,24903,24896,24888,24881,24874,24866,24859,24852,24844,24837,24830,24822,24815,24808,24800,24793,24786,24778,24771,24763,24756,24749,24741,24734,24727,24719,24712,24704,24697,24690,24682,24675,24667,24660,24653,24645,24638,24630,24623,24616,24608,24601,24593,24586,24578, +24571,24564,24556,24549,24541,24534,24526,24519,24512,24504,24497,24489,24482,24474,24467,24459,24452,24444,24437,24429,24422,24414,24407,24400,24392,24385,24377,24370,24362,24355,24347,24340,24332,24325,24317,24310,24302,24295,24287,24280,24272,24264,24257,24249,24242,24234,24227,24219,24212,24204, +24197,24189,24182,24174,24167,24159,24151,24144,24136,24129,24121,24114,24106,24098,24091,24083,24076,24068,24061,24053,24045,24038,24030,24023,24015,24008,24000,23992,23985,23977,23970,23962,23954,23947,23939,23931,23924,23916,23909,23901,23893,23886,23878,23870,23863,23855,23848,23840,23832,23825, +23817,23809,23802,23794,23786,23779,23771,23763,23756,23748,23740,23733,23725,23717,23710,23702,23694,23687,23679,23671,23664,23656,23648,23641,23633,23625,23618,23610,23602,23594,23587,23579,23571,23564,23556,23548,23540,23533,23525,23517,23510,23502,23494,23486,23479,23471,23463,23455,23448,23440, +23432,23424,23417,23409,23401,23393,23386,23378,23370,23362,23355,23347,23339,23331,23324,23316,23308,23300,23293,23285,23277,23269,23261,23254,23246,23238,23230,23222,23215,23207,23199,23191,23183,23176,23168,23160,23152,23144,23137,23129,23121,23113,23105,23097,23090,23082,23074,23066,23058,23050, +23043,23035,23027,23019,23011,23003,22996,22988,22980,22972,22964,22956,22948,22941,22933,22925,22917,22909,22901,22893,22885,22878,22870,22862,22854,22846,22838,22830,22822,22815,22807,22799,22791,22783,22775,22767,22759,22751,22743,22736,22728,22720,22712,22704,22696,22688,22680,22672,22664,22656, +22648,22641,22633,22625,22617,22609,22601,22593,22585,22577,22569,22561,22553,22545,22537,22529,22521,22513,22505,22498,22490,22482,22474,22466,22458,22450,22442,22434,22426,22418,22410,22402,22394,22386,22378,22370,22362,22354,22346,22338,22330,22322,22314,22306,22298,22290,22282,22274,22266,22258, +22250,22242,22234,22226,22218,22210,22202,22194,22186,22178,22170,22162,22154,22146,22138,22130,22122,22114,22106,22097,22089,22081,22073,22065,22057,22049,22041,22033,22025,22017,22009,22001,21993,21985,21977,21969,21961,21952,21944,21936,21928,21920,21912,21904,21896,21888,21880,21872,21864,21856, +21847,21839,21831,21823,21815,21807,21799,21791,21783,21775,21767,21758,21750,21742,21734,21726,21718,21710,21702,21694,21685,21677,21669,21661,21653,21645,21637,21629,21620,21612,21604,21596,21588,21580,21572,21563,21555,21547,21539,21531,21523,21515,21506,21498,21490,21482,21474,21466,21458,21449, +21441,21433,21425,21417,21409,21400,21392,21384,21376,21368,21360,21351,21343,21335,21327,21319,21310,21302,21294,21286,21278,21270,21261,21253,21245,21237,21229,21220,21212,21204,21196,21188,21179,21171,21163,21155,21147,21138,21130,21122,21114,21106,21097,21089,21081,21073,21064,21056,21048,21040, +21032,21023,21015,21007,20999,20990,20982,20974,20966,20957,20949,20941,20933,20924,20916,20908,20900,20891,20883,20875,20867,20858,20850,20842,20834,20825,20817,20809,20801,20792,20784,20776,20768,20759,20751,20743,20735,20726,20718,20710,20701,20693,20685,20677,20668,20660,20652,20643,20635,20627, +20619,20610,20602,20594,20585,20577,20569,20561,20552,20544,20536,20527,20519,20511,20502,20494,20486,20478,20469,20461,20453,20444,20436,20428,20419,20411,20403,20394,20386,20378,20369,20361,20353,20345,20336,20328,20320,20311,20303,20295,20286,20278,20270,20261,20253,20245,20236,20228,20220,20211, +20203,20194,20186,20178,20169,20161,20153,20144,20136,20128,20119,20111,20103,20094,20086,20078,20069,20061,20052,20044,20036,20027,20019,20011,20002,19994,19986,19977,19969,19960,19952,19944,19935,19927,19919,19910,19902,19893,19885,19877,19868,19860,19852,19843,19835,19826,19818,19810,19801,19793, +19784,19776,19768,19759,19751,19742,19734,19726,19717,19709,19700,19692,19684,19675,19667,19658,19650,19642,19633,19625,19616,19608,19600,19591,19583,19574,19566,19557,19549,19541,19532,19524,19515,19507,19499,19490,19482,19473,19465,19456,19448,19440,19431,19423,19414,19406,19397,19389,19381,19372, +19364,19355,19347,19338,19330,19321,19313,19305,19296,19288,19279,19271,19262,19254,19245,19237,19229,19220,19212,19203,19195,19186,19178,19169,19161,19153,19144,19136,19127,19119,19110,19102,19093,19085,19076,19068,19059,19051,19043,19034,19026,19017,19009,19000,18992,18983,18975,18966,18958,18949, +18941,18932,18924,18915,18907,18898,18890,18882,18873,18865,18856,18848,18839,18831,18822,18814,18805,18797,18788,18780,18771,18763,18754,18746,18737,18729,18720,18712,18703,18695,18686,18678,18669,18661,18652,18644,18635,18627,18618,18610,18601,18593,18584,18576,18567,18559,18550,18542,18533,18525, +18516,18508,18499,18491,18482,18474,18465,18457,18448,18440,18431,18423,18414,18406,18397,18389,18380,18372,18363,18355,18346,18338,18329,18321,18312,18304,18295,18286,18278,18269,18261,18252,18244,18235,18227,18218,18210,18201,18193,18184,18176,18167,18159,18150,18142,18133,18124,18116,18107,18099, +18090,18082,18073,18065,18056,18048,18039,18031,18022,18014,18005,17996,17988,17979,17971,17962,17954,17945,17937,17928,17920,17911,17903,17894,17885,17877,17868,17860,17851,17843,17834,17826,17817,17809,17800,17791,17783,17774,17766,17757,17749,17740,17732,17723,17714,17706,17697,17689,17680,17672, +17663,17655,17646,17638,17629,17620,17612,17603,17595,17586,17578,17569,17561,17552,17543,17535,17526,17518,17509,17501,17492,17483,17475,17466,17458,17449,17441,17432,17424,17415,17406,17398,17389,17381,17372,17364,17355,17346,17338,17329,17321,17312,17304,17295,17287,17278,17269,17261,17252,17244, +17235,17227,17218,17209,17201,17192,17184,17175,17167,17158,17149,17141,17132,17124,17115,17107,17098,17089,17081,17072,17064,17055,17047,17038,17029,17021,17012,17004,16995,16987,16978,16969,16961,16952,16944,16935,16927,16918,16909,16901,16892,16884,16875,16867,16858,16849,16841,16832,16824,16815, +16806,16798,16789,16781,16772,16764,16755,16746,16738,16729,16721,16712,16704,16695,16686,16678,16669,16661,16652,16644,16635,16626,16618,16609,16601,16592,16583,16575,16566,16558,16549,16541,16532,16523,16515,16506,16498,16489,16481,16472,16463,16455,16446,16438,16429,16420,16412,16403,16395,16386, +16378,16369,16360,16352,16343,16335,16326,16318,16309,16300,16292,16283,16275,16266,16257,16249,16240,16232,16223,16215,16206,16197,16189,16180,16172,16163,16155,16146,16137,16129,16120,16112,16103,16094,16086,16077,16069,16060,16052,16043,16034,16026,16017,16009,16000,15992,15983,15974,15966,15957, +15949,15940,15931,15923,15914,15906,15897,15889,15880,15871,15863,15854,15846,15837,15829,15820,15811,15803,15794,15786,15777,15769,15760,15751,15743,15734,15726,15717,15709,15700,15691,15683,15674,15666,15657,15649,15640,15631,15623,15614,15606,15597,15589,15580,15571,15563,15554,15546,15537,15529, +15520,15511,15503,15494,15486,15477,15469,15460,15451,15443,15434,15426,15417,15409,15400,15392,15383,15374,15366,15357,15349,15340,15332,15323,15314,15306,15297,15289,15280,15272,15263,15255,15246,15237,15229,15220,15212,15203,15195,15186,15178,15169,15160,15152,15143,15135,15126,15118,15109,15101, +15092,15083,15075,15066,15058,15049,15041,15032,15024,15015,15006,14998,14989,14981,14972,14964,14955,14947,14938,14930,14921,14912,14904,14895,14887,14878,14870,14861,14853,14844,14836,14827,14819,14810,14801,14793,14784,14776,14767,14759,14750,14742,14733,14725,14716,14708,14699,14690,14682,14673, +14665,14656,14648,14639,14631,14622,14614,14605,14597,14588,14580,14571,14563,14554,14545,14537,14528,14520,14511,14503,14494,14486,14477,14469,14460,14452,14443,14435,14426,14418,14409,14401,14392,14384,14375,14367,14358,14350,14341,14332,14324,14315,14307,14298,14290,14281,14273,14264,14256,14247, +14239,14230,14222,14213,14205,14196,14188,14179,14171,14162,14154,14145,14137,14128,14120,14111,14103,14094,14086,14077,14069,14060,14052,14043,14035,14026,14018,14009,14001,13992,13984,13976,13967,13959,13950,13942,13933,13925,13916,13908,13899,13891,13882,13874,13865,13857,13848,13840,13831,13823, +13814,13806,13797,13789,13781,13772,13764,13755,13747,13738,13730,13721,13713,13704,13696,13687,13679,13670,13662,13654,13645,13637,13628,13620,13611,13603,13594,13586,13577,13569,13561,13552,13544,13535,13527,13518,13510,13501,13493,13485,13476,13468,13459,13451,13442,13434,13425,13417,13409,13400, +13392,13383,13375,13366,13358,13350,13341,13333,13324,13316,13307,13299,13291,13282,13274,13265,13257,13248,13240,13232,13223,13215,13206,13198,13189,13181,13173,13164,13156,13147,13139,13131,13122,13114,13105,13097,13089,13080,13072,13063,13055,13047,13038,13030,13021,13013,13005,12996,12988,12979, +12971,12963,12954,12946,12937,12929,12921,12912,12904,12896,12887,12879,12870,12862,12854,12845,12837,12828,12820,12812,12803,12795,12787,12778,12770,12762,12753,12745,12736,12728,12720,12711,12703,12695,12686,12678,12670,12661,12653,12644,12636,12628,12619,12611,12603,12594,12586,12578,12569,12561, +12553,12544,12536,12528,12519,12511,12503,12494,12486,12478,12469,12461,12453,12444,12436,12428,12419,12411,12403,12394,12386,12378,12369,12361,12353,12344,12336,12328,12320,12311,12303,12295,12286,12278,12270,12261,12253,12245,12236,12228,12220,12212,12203,12195,12187,12178,12170,12162,12154,12145, +12137,12129,12120,12112,12104,12096,12087,12079,12071,12062,12054,12046,12038,12029,12021,12013,12005,11996,11988,11980,11971,11963,11955,11947,11938,11930,11922,11914,11905,11897,11889,11881,11872,11864,11856,11848,11839,11831,11823,11815,11806,11798,11790,11782,11774,11765,11757,11749,11741,11732, +11724,11716,11708,11699,11691,11683,11675,11667,11658,11650,11642,11634,11626,11617,11609,11601,11593,11585,11576,11568,11560,11552,11544,11535,11527,11519,11511,11503,11494,11486,11478,11470,11462,11453,11445,11437,11429,11421,11413,11404,11396,11388,11380,11372,11364,11355,11347,11339,11331,11323, +11315,11306,11298,11290,11282,11274,11266,11257,11249,11241,11233,11225,11217,11209,11200,11192,11184,11176,11168,11160,11152,11144,11135,11127,11119,11111,11103,11095,11087,11079,11070,11062,11054,11046,11038,11030,11022,11014,11006,10997,10989,10981,10973,10965,10957,10949,10941,10933,10925,10916, +10908,10900,10892,10884,10876,10868,10860,10852,10844,10836,10828,10820,10811,10803,10795,10787,10779,10771,10763,10755,10747,10739,10731,10723,10715,10707,10699,10691,10683,10675,10667,10658,10650,10642,10634,10626,10618,10610,10602,10594,10586,10578,10570,10562,10554,10546,10538,10530,10522,10514, +10506,10498,10490,10482,10474,10466,10458,10450,10442,10434,10426,10418,10410,10402,10394,10386,10378,10370,10362,10354,10346,10338,10330,10322,10314,10306,10298,10290,10282,10274,10267,10259,10251,10243,10235,10227,10219,10211,10203,10195,10187,10179,10171,10163,10155,10147,10139,10131,10124,10116, +10108,10100,10092,10084,10076,10068,10060,10052,10044,10036,10029,10021,10013,10005,9997,9989,9981,9973,9965,9957,9950,9942,9934,9926,9918,9910,9902,9894,9886,9879,9871,9863,9855,9847,9839,9831,9824,9816,9808,9800,9792,9784,9776,9769,9761,9753,9745,9737,9729,9721, +9714,9706,9698,9690,9682,9674,9667,9659,9651,9643,9635,9628,9620,9612,9604,9596,9588,9581,9573,9565,9557,9549,9542,9534,9526,9518,9511,9503,9495,9487,9479,9472,9464,9456,9448,9440,9433,9425,9417,9409,9402,9394,9386,9378,9371,9363,9355,9347,9340,9332, +9324,9316,9309,9301,9293,9285,9278,9270,9262,9255,9247,9239,9231,9224,9216,9208,9201,9193,9185,9177,9170,9162,9154,9147,9139,9131,9124,9116,9108,9100,9093,9085,9077,9070,9062,9054,9047,9039,9031,9024,9016,9008,9001,8993,8985,8978,8970,8962,8955,8947, +8940,8932,8924,8917,8909,8901,8894,8886,8878,8871,8863,8856,8848,8840,8833,8825,8817,8810,8802,8795,8787,8779,8772,8764,8757,8749,8742,8734,8726,8719,8711,8704,8696,8688,8681,8673,8666,8658,8651,8643,8635,8628,8620,8613,8605,8598,8590,8583,8575,8568, +8560,8552,8545,8537,8530,8522,8515,8507,8500,8492,8485,8477,8470,8462,8455,8447,8440,8432,8425,8417,8410,8402,8395,8387,8380,8372,8365,8357,8350,8342,8335,8327,8320,8312,8305,8297,8290,8283,8275,8268,8260,8253,8245,8238,8230,8223,8216,8208,8201,8193, +8186,8178,8171,8164,8156,8149,8141,8134,8126,8119,8112,8104,8097,8089,8082,8075,8067,8060,8052,8045,8038,8030,8023,8016,8008,8001,7993,7986,7979,7971,7964,7957,7949,7942,7935,7927,7920,7913,7905,7898,7891,7883,7876,7869,7861,7854,7847,7839,7832,7825, +7817,7810,7803,7795,7788,7781,7773,7766,7759,7752,7744,7737,7730,7722,7715,7708,7701,7693,7686,7679,7671,7664,7657,7650,7642,7635,7628,7621,7613,7606,7599,7592,7584,7577,7570,7563,7556,7548,7541,7534,7527,7519,7512,7505,7498,7491,7483,7476,7469,7462, +7455,7447,7440,7433,7426,7419,7411,7404,7397,7390,7383,7376,7368,7361,7354,7347,7340,7333,7326,7318,7311,7304,7297,7290,7283,7276,7268,7261,7254,7247,7240,7233,7226,7219,7211,7204,7197,7190,7183,7176,7169,7162,7155,7148,7140,7133,7126,7119,7112,7105, +7098,7091,7084,7077,7070,7063,7056,7049,7042,7035,7027,7020,7013,7006,6999,6992,6985,6978,6971,6964,6957,6950,6943,6936,6929,6922,6915,6908,6901,6894,6887,6880,6873,6866,6859,6852,6845,6838,6831,6824,6817,6810,6803,6796,6790,6783,6776,6769,6762,6755, +6748,6741,6734,6727,6720,6713,6706,6699,6692,6685,6679,6672,6665,6658,6651,6644,6637,6630,6623,6616,6610,6603,6596,6589,6582,6575,6568,6561,6555,6548,6541,6534,6527,6520,6513,6507,6500,6493,6486,6479,6472,6466,6459,6452,6445,6438,6431,6425,6418,6411, +6404,6397,6391,6384,6377,6370,6363,6357,6350,6343,6336,6330,6323,6316,6309,6302,6296,6289,6282,6275,6269,6262,6255,6248,6242,6235,6228,6222,6215,6208,6201,6195,6188,6181,6174,6168,6161,6154,6148,6141,6134,6128,6121,6114,6108,6101,6094,6088,6081,6074, +6067,6061,6054,6048,6041,6034,6028,6021,6014,6008,6001,5994,5988,5981,5974,5968,5961,5955,5948,5941,5935,5928,5922,5915,5908,5902,5895,5889,5882,5875,5869,5862,5856,5849,5843,5836,5829,5823,5816,5810,5803,5797,5790,5784,5777,5770,5764,5757,5751,5744, +5738,5731,5725,5718,5712,5705,5699,5692,5686,5679,5673,5666,5660,5653,5647,5640,5634,5627,5621,5614,5608,5602,5595,5589,5582,5576,5569,5563,5556,5550,5544,5537,5531,5524,5518,5511,5505,5499,5492,5486,5479,5473,5467,5460,5454,5447,5441,5435,5428,5422, +5415,5409,5403,5396,5390,5384,5377,5371,5365,5358,5352,5346,5339,5333,5327,5320,5314,5308,5301,5295,5289,5282,5276,5270,5263,5257,5251,5244,5238,5232,5226,5219,5213,5207,5201,5194,5188,5182,5175,5169,5163,5157,5150,5144,5138,5132,5125,5119,5113,5107, +5101,5094,5088,5082,5076,5070,5063,5057,5051,5045,5039,5032,5026,5020,5014,5008,5001,4995,4989,4983,4977,4971,4964,4958,4952,4946,4940,4934,4928,4921,4915,4909,4903,4897,4891,4885,4879,4873,4866,4860,4854,4848,4842,4836,4830,4824,4818,4812,4806,4800, +4793,4787,4781,4775,4769,4763,4757,4751,4745,4739,4733,4727,4721,4715,4709,4703,4697,4691,4685,4679,4673,4667,4661,4655,4649,4643,4637,4631,4625,4619,4613,4607,4601,4595,4589,4583,4577,4571,4565,4559,4553,4548,4542,4536,4530,4524,4518,4512,4506,4500, +4494,4488,4482,4477,4471,4465,4459,4453,4447,4441,4435,4430,4424,4418,4412,4406,4400,4394,4389,4383,4377,4371,4365,4359,4354,4348,4342,4336,4330,4324,4319,4313,4307,4301,4295,4290,4284,4278,4272,4267,4261,4255,4249,4243,4238,4232,4226,4220,4215,4209, +4203,4198,4192,4186,4180,4175,4169,4163,4157,4152,4146,4140,4135,4129,4123,4118,4112,4106,4101,4095,4089,4083,4078,4072,4067,4061,4055,4050,4044,4038,4033,4027,4021,4016,4010,4004,3999,3993,3988,3982,3976,3971,3965,3960,3954,3948,3943,3937,3932,3926, +3921,3915,3909,3904,3898,3893,3887,3882,3876,3871,3865,3860,3854,3848,3843,3837,3832,3826,3821,3815,3810,3804,3799,3793,3788,3782,3777,3771,3766,3761,3755,3750,3744,3739,3733,3728,3722,3717,3711,3706,3701,3695,3690,3684,3679,3673,3668,3663,3657,3652, +3646,3641,3636,3630,3625,3619,3614,3609,3603,3598,3593,3587,3582,3577,3571,3566,3561,3555,3550,3545,3539,3534,3529,3523,3518,3513,3507,3502,3497,3491,3486,3481,3476,3470,3465,3460,3454,3449,3444,3439,3433,3428,3423,3418,3412,3407,3402,3397,3391,3386, +3381,3376,3371,3365,3360,3355,3350,3345,3339,3334,3329,3324,3319,3313,3308,3303,3298,3293,3288,3282,3277,3272,3267,3262,3257,3252,3247,3241,3236,3231,3226,3221,3216,3211,3206,3201,3195,3190,3185,3180,3175,3170,3165,3160,3155,3150,3145,3140,3135,3130, +3125,3119,3114,3109,3104,3099,3094,3089,3084,3079,3074,3069,3064,3059,3054,3049,3044,3039,3034,3029,3024,3020,3015,3010,3005,3000,2995,2990,2985,2980,2975,2970,2965,2960,2955,2950,2945,2941,2936,2931,2926,2921,2916,2911,2906,2901,2897,2892,2887,2882, +2877,2872,2867,2863,2858,2853,2848,2843,2838,2834,2829,2824,2819,2814,2810,2805,2800,2795,2790,2786,2781,2776,2771,2766,2762,2757,2752,2747,2743,2738,2733,2728,2724,2719,2714,2709,2705,2700,2695,2691,2686,2681,2676,2672,2667,2662,2658,2653,2648,2644, +2639,2634,2630,2625,2620,2616,2611,2606,2602,2597,2592,2588,2583,2579,2574,2569,2565,2560,2556,2551,2546,2542,2537,2533,2528,2523,2519,2514,2510,2505,2501,2496,2492,2487,2482,2478,2473,2469,2464,2460,2455,2451,2446,2442,2437,2433,2428,2424,2419,2415, +2410,2406,2401,2397,2392,2388,2383,2379,2375,2370,2366,2361,2357,2352,2348,2343,2339,2335,2330,2326,2321,2317,2313,2308,2304,2299,2295,2291,2286,2282,2278,2273,2269,2265,2260,2256,2251,2247,2243,2238,2234,2230,2226,2221,2217,2213,2208,2204,2200,2195, +2191,2187,2183,2178,2174,2170,2165,2161,2157,2153,2148,2144,2140,2136,2131,2127,2123,2119,2115,2110,2106,2102,2098,2094,2089,2085,2081,2077,2073,2068,2064,2060,2056,2052,2048,2043,2039,2035,2031,2027,2023,2019,2015,2010,2006,2002,1998,1994,1990,1986, +1982,1978,1974,1969,1965,1961,1957,1953,1949,1945,1941,1937,1933,1929,1925,1921,1917,1913,1909,1905,1901,1897,1893,1889,1885,1881,1877,1873,1869,1865,1861,1857,1853,1849,1845,1841,1837,1833,1829,1825,1821,1817,1813,1809,1806,1802,1798,1794,1790,1786, +1782,1778,1774,1770,1767,1763,1759,1755,1751,1747,1743,1740,1736,1732,1728,1724,1720,1717,1713,1709,1705,1701,1698,1694,1690,1686,1682,1679,1675,1671,1667,1663,1660,1656,1652,1648,1645,1641,1637,1633,1630,1626,1622,1619,1615,1611,1607,1604,1600,1596, +1593,1589,1585,1582,1578,1574,1571,1567,1563,1560,1556,1552,1549,1545,1541,1538,1534,1530,1527,1523,1520,1516,1512,1509,1505,1502,1498,1494,1491,1487,1484,1480,1477,1473,1470,1466,1462,1459,1455,1452,1448,1445,1441,1438,1434,1431,1427,1424,1420,1417, +1413,1410,1406,1403,1399,1396,1392,1389,1385,1382,1379,1375,1372,1368,1365,1361,1358,1355,1351,1348,1344,1341,1338,1334,1331,1327,1324,1321,1317,1314,1310,1307,1304,1300,1297,1294,1290,1287,1284,1280,1277,1274,1270,1267,1264,1261,1257,1254,1251,1247, +1244,1241,1238,1234,1231,1228,1224,1221,1218,1215,1211,1208,1205,1202,1199,1195,1192,1189,1186,1183,1179,1176,1173,1170,1167,1163,1160,1157,1154,1151,1148,1144,1141,1138,1135,1132,1129,1126,1122,1119,1116,1113,1110,1107,1104,1101,1098,1095,1091,1088, +1085,1082,1079,1076,1073,1070,1067,1064,1061,1058,1055,1052,1049,1046,1043,1040,1037,1034,1031,1028,1025,1022,1019,1016,1013,1010,1007,1004,1001,998,995,992,989,986,983,980,978,975,972,969,966,963,960,957,954,951,949,946,943,940, +937,934,931,928,926,923,920,917,914,911,909,906,903,900,897,895,892,889,886,883,881,878,875,872,870,867,864,861,859,856,853,850,848,845,842,840,837,834,831,829,826,823,821,818,815,813,810,807,805,802, +799,797,794,791,789,786,784,781,778,776,773,771,768,765,763,760,758,755,752,750,747,745,742,740,737,735,732,729,727,724,722,719,717,714,712,709,707,704,702,699,697,694,692,690,687,685,682,680,677,675, +672,670,668,665,663,660,658,655,653,651,648,646,643,641,639,636,634,632,629,627,625,622,620,618,615,613,611,608,606,604,601,599,597,594,592,590,588,585,583,581,579,576,574,572,570,567,565,563,561,558, +556,554,552,550,547,545,543,541,539,536,534,532,530,528,526,523,521,519,517,515,513,511,508,506,504,502,500,498,496,494,492,490,487,485,483,481,479,477,475,473,471,469,467,465,463,461,459,457,455,453, +451,449,447,445,443,441,439,437,435,433,431,429,427,425,423,421,419,417,415,414,412,410,408,406,404,402,400,398,397,395,393,391,389,387,385,383,382,380,378,376,374,373,371,369,367,365,363,362,360,358, +356,355,353,351,349,347,346,344,342,340,339,337,335,334,332,330,328,327,325,323,322,320,318,317,315,313,312,310,308,307,305,303,302,300,298,297,295,293,292,290,289,287,285,284,282,281,279,278,276,274, +273,271,270,268,267,265,264,262,261,259,257,256,254,253,251,250,248,247,245,244,243,241,240,238,237,235,234,232,231,229,228,227,225,224,222,221,220,218,217,215,214,213,211,210,209,207,206,204,203,202, +200,199,198,196,195,194,192,191,190,189,187,186,185,183,182,181,180,178,177,176,175,173,172,171,170,168,167,166,165,163,162,161,160,159,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140, +139,138,137,136,135,134,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,92,91,90, +89,88,87,86,85,84,84,83,82,81,80,79,78,78,77,76,75,74,74,73,72,71,70,70,69,68,67,66,66,65,64,63,63,62,61,60,60,59,58,57,57,56,55,55,54,53,53,52,51,51, +50,49,49,48,47,47,46,45,45,44,43,43,42,42,41,40,40,39,39,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,28,27,27,26,26,25,25,24,24,23,23,22, +22,22,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6, +5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +#endif \ No newline at end of file From 74228c506ebbd303aae3e2e931d683ef4466e9b3 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 15:24:01 +0100 Subject: [PATCH 009/119] Add stubs for FM classes. --- FM.cpp | 38 ++++++++++++++++++++++++++++++++++--- FM.h | 35 ++++++++++++++++++++++++++++++++-- FMCTCSSTX.cpp | 35 ++++++++++++++++++++++++++++++++++ FMCTCSSTX.h | 36 +++++++++++++++++++++++++++++++++++ FMGoertzel.cpp | 33 ++++++++++++++++++++++++++++++++ FMGoertzel.h | 35 ++++++++++++++++++++++++++++++++++ FMKeyer.cpp | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ FMKeyer.h | 43 ++++++++++++++++++++++++++++++++++++++++++ FMTimeout.cpp | 35 ++++++++++++++++++++++++++++++++++ FMTimeout.h | 36 +++++++++++++++++++++++++++++++++++ FMTimer.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ FMTimer.h | 43 ++++++++++++++++++++++++++++++++++++++++++ SerialPort.cpp | 2 +- 13 files changed, 465 insertions(+), 6 deletions(-) create mode 100644 FMCTCSSTX.cpp create mode 100644 FMCTCSSTX.h create mode 100644 FMGoertzel.cpp create mode 100644 FMGoertzel.h create mode 100644 FMKeyer.cpp create mode 100644 FMKeyer.h create mode 100644 FMTimeout.cpp create mode 100644 FMTimeout.h create mode 100644 FMTimer.cpp create mode 100644 FMTimer.h diff --git a/FM.cpp b/FM.cpp index 2fc7909..08e4766 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,7 +20,22 @@ #include "Globals.h" #include "FM.h" -CFM::CFM() +CFM::CFM() : +m_callsign(), +m_rfAck(), +m_goertzel(), +m_ctcss(), +m_timeoutTone(), +m_state(FS_LISTENING), +m_callsignAtStart(false), +m_callsignAtEnd(false), +m_callsignTimer(), +m_timeoutTimer(), +m_holdoffTimer(), +m_kerchunkTimer(), +m_ackMinTimer(), +m_ackDelayTimer(), +m_hangTimer() { } @@ -36,14 +51,31 @@ void CFM::reset() { } -void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd) +void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd) { + m_callsign.setParams(callsign, speed, frequency, lowLevel); + + m_callsignAtStart = callsignAtStart; + m_callsignAtEnd = callsignAtEnd; + + m_holdoffTimer.setTimeout(holdoff); } void CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) { + m_rfAck.setParams(rfAck, speed, frequency, level); + + m_ackDelayTimer.setTimeout(delay); + m_ackMinTimer.setTimeout(minTime); } -void CFM::setMisc(const char* netAck, uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) +void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) { + m_timeoutTone.setParams(timeoutLevel); + m_goertzel.setParams(ctcssFrequency, ctcssThreshold); + m_ctcss.setParams(ctcssFrequency, ctcssLevel); + + m_timeoutTimer.setTimeout(timeout); + m_kerchunkTimer.setTimeout(kerchunkTime); + m_hangTimer.setTimeout(hangTime); } diff --git a/FM.h b/FM.h index ec2cb6f..2ee6aa8 100644 --- a/FM.h +++ b/FM.h @@ -21,6 +21,22 @@ #include "Config.h" +#include "FMGoertzel.h" +#include "FMCTCSSTX.h" +#include "FMTimeout.h" +#include "FMKeyer.h" +#include "FMTimer.h" + +enum FM_STATE { + FS_LISTENING, + FS_KERCHUNK, + FS_RELAYING_RF, + FS_RELAYING_WAIT_RF, + FS_TIMEOUT_RF, + FS_TIMEOUT_WAIT_RF, + FS_HANG +}; + class CFM { public: CFM(); @@ -31,11 +47,26 @@ public: void reset(); - void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callAtStart, bool callAtEnd); + void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); void setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - void setMisc(const char* netAck, uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); + void setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: + CFMKeyer m_callsign; + CFMKeyer m_rfAck; + CFMGoertzel m_goertzel; + CFMCTCSSTX m_ctcss; + CFMTimeout m_timeoutTone; + FM_STATE m_state; + bool m_callsignAtStart; + bool m_callsignAtEnd; + CFMTimer m_callsignTimer; + CFMTimer m_timeoutTimer; + CFMTimer m_holdoffTimer; + CFMTimer m_kerchunkTimer; + CFMTimer m_ackMinTimer; + CFMTimer m_ackDelayTimer; + CFMTimer m_hangTimer; }; #endif diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp new file mode 100644 index 0000000..8c98856 --- /dev/null +++ b/FMCTCSSTX.cpp @@ -0,0 +1,35 @@ +/* + * 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 "FMCTCSSTX.h" + +CFMCTCSSTX::CFMCTCSSTX() : +m_level(128 * 128) +{ +} + +void CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) +{ + m_level = q15_t(level * 128); +} + +void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) +{ +} diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h new file mode 100644 index 0000000..906b363 --- /dev/null +++ b/FMCTCSSTX.h @@ -0,0 +1,36 @@ +/* + * 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(FMCTCSSTX_H) +#define FMCTCSSTX_H + +#include "Config.h" + +class CFMCTCSSTX { +public: + CFMCTCSSTX(); + + void setParams(uint8_t frequency, uint8_t level); + + void getAudio(q15_t* samples, uint8_t length); + +private: + q15_t m_level; +}; + +#endif diff --git a/FMGoertzel.cpp b/FMGoertzel.cpp new file mode 100644 index 0000000..787047a --- /dev/null +++ b/FMGoertzel.cpp @@ -0,0 +1,33 @@ +/* + * 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 "FMGoertzel.h" + +CFMGoertzel::CFMGoertzel() +{ +} + +void CFMGoertzel::setParams(uint8_t frequency, uint8_t threshold) +{ +} + +bool CFMGoertzel::process(const q15_t* samples, uint8_t length) +{ +} diff --git a/FMGoertzel.h b/FMGoertzel.h new file mode 100644 index 0000000..5d0330e --- /dev/null +++ b/FMGoertzel.h @@ -0,0 +1,35 @@ +/* + * 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(FMGoertzel_H) +#define FMGoertzel_H + +#include "Config.h" + +class CFMGoertzel { +public: + CFMGoertzel(); + + void setParams(uint8_t frequency, uint8_t threshold); + + bool process(const q15_t* samples, uint8_t length); + +private: +}; + +#endif diff --git a/FMKeyer.cpp b/FMKeyer.cpp new file mode 100644 index 0000000..a3eb181 --- /dev/null +++ b/FMKeyer.cpp @@ -0,0 +1,51 @@ +/* + * 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 "FMKeyer.h" + +CFMKeyer::CFMKeyer() : +m_level(128 * 128), +m_wanted(false), +m_running(false) +{ +} + +void CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) +{ + m_level = q15_t(level * 128); +} + +void CFMKeyer::getAudio(q15_t* samples, uint8_t length) +{ +} + +void CFMKeyer::start() +{ + m_wanted = true; +} + +void CFMKeyer::stop() +{ +} + +bool CFMKeyer::isRunning() const +{ + return m_running; +} diff --git a/FMKeyer.h b/FMKeyer.h new file mode 100644 index 0000000..e0b3ca8 --- /dev/null +++ b/FMKeyer.h @@ -0,0 +1,43 @@ +/* + * 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(FMKeyer_H) +#define FMKeyer_H + +#include "Config.h" + +class CFMKeyer { +public: + CFMKeyer(); + + void setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level); + + void getAudio(q15_t* samples, uint8_t length); + + void start(); + void stop(); + + bool isRunning() const; + +private: + q15_t m_level; + bool m_wanted; + bool m_running; +}; + +#endif diff --git a/FMTimeout.cpp b/FMTimeout.cpp new file mode 100644 index 0000000..06cf1ee --- /dev/null +++ b/FMTimeout.cpp @@ -0,0 +1,35 @@ +/* + * 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 "FMTimeout.h" + +CFMTimeout::CFMTimeout() : +m_level(128 * 128) +{ +} + +void CFMTimeout::setParams(uint8_t level) +{ + m_level = q15_t(level * 128); +} + +void CFMTimeout::getAudio(q15_t* samples, uint8_t length) +{ +} diff --git a/FMTimeout.h b/FMTimeout.h new file mode 100644 index 0000000..48c179a --- /dev/null +++ b/FMTimeout.h @@ -0,0 +1,36 @@ +/* + * 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(FMTimeout_H) +#define FMTimeout_H + +#include "Config.h" + +class CFMTimeout { +public: + CFMTimeout(); + + void setParams(uint8_t level); + + void getAudio(q15_t* samples, uint8_t length); + +private: + q15_t m_level; +}; + +#endif diff --git a/FMTimer.cpp b/FMTimer.cpp new file mode 100644 index 0000000..b839db4 --- /dev/null +++ b/FMTimer.cpp @@ -0,0 +1,49 @@ +/* + * 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 "FMTimer.h" + +CFMTimer::CFMTimer() +{ +} + +void CFMTimer::setTimeout(uint16_t time) +{ +} + +void CFMTimer::start() +{ +} + +void CFMTimer::stop() +{ +} + +void CFMTimer::clock() +{ +} + +bool CFMTimer::isRunning() const +{ +} + +bool CFMTimer::hasExpired() const +{ +} diff --git a/FMTimer.h b/FMTimer.h new file mode 100644 index 0000000..a8b91c0 --- /dev/null +++ b/FMTimer.h @@ -0,0 +1,43 @@ +/* + * 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(FMTimer_H) +#define FMTimer_H + +#include "Config.h" + +class CFMTimer { +public: + CFMTimer(); + + void setTimeout(uint16_t time); + + void start(); + + void stop(); + + void clock(); + + bool isRunning() const; + + bool hasExpired() const; + +private: +}; + +#endif diff --git a/SerialPort.cpp b/SerialPort.cpp index a942159..e893b0e 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -432,7 +432,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) ack[n] = data[i]; ack[n] = '\0'; - fm.setMisc(ack, timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); + fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); return 0U; } From db6fde90e011432c3214dc93fa8dd700ab7ad6fc Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 16:31:49 +0100 Subject: [PATCH 010/119] Add the intial state machine. --- FM.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++++++++- FM.h | 22 +++-- FMTimeout.cpp | 13 ++- FMTimeout.h | 4 + FMTimer.cpp | 11 ++- FMTimer.h | 5 +- 6 files changed, 281 insertions(+), 10 deletions(-) diff --git a/FM.cpp b/FM.cpp index 08e4766..fd0a4f2 100644 --- a/FM.cpp +++ b/FM.cpp @@ -39,8 +39,38 @@ m_hangTimer() { } -void CFM::samples(bool cos, const q15_t* samples, uint8_t length) +void CFM::samples(bool cos, q15_t* samples, uint8_t length) { + // De-emphasis + + bool ctcss = m_goertzel.process(samples, length); + + bool validSignal = ctcss && cos; + + stateMachine(validSignal); + + if (m_modemState != STATE_FM) + return; + + // Only let audio through when relaying audio + if (m_state != FS_RELAYING) { + for (uint8_t i = 0U; i < length; i++) + samples[i] = 0; + } + + m_rfAck.getAudio(samples, length); + + m_callsign.getAudio(samples, length); + + m_timeoutTone.getAudio(samples, length); + + // Band-pass filter + + m_ctcss.getAudio(samples, length); + + // Pre-emphasis + + io.write(STATE_FM, samples, length); } void CFM::process() @@ -79,3 +109,207 @@ void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency m_kerchunkTimer.setTimeout(kerchunkTime); m_hangTimer.setTimeout(hangTime); } + +void CFM::stateMachine(bool validSignal) +{ + m_callsignTimer.clock(); + m_timeoutTimer.clock(); + m_holdoffTimer.clock(); + m_kerchunkTimer.clock(); + m_ackMinTimer.clock(); + m_ackDelayTimer.clock(); + m_hangTimer.clock(); + + switch (m_state) { + case FS_LISTENING: + listeningState(validSignal); + break; + case FS_KERCHUNK: + kerchunkState(validSignal); + break; + case FS_RELAYING: + relayingState(validSignal); + break; + case FS_RELAYING_WAIT: + relayingWaitState(validSignal); + break; + case FS_TIMEOUT: + timeoutState(validSignal); + break; + case FS_TIMEOUT_WAIT: + timeoutWaitState(validSignal); + break; + case FS_HANG: + hangState(validSignal); + break; + default: + break; + } +} + +void CFM::listeningState(bool validSignal) +{ + if (m_kerchunkTimer.getTimeout() > 0U) { + m_state = FS_KERCHUNK; + m_kerchunkTimer.start(); + } else { + m_state = FS_RELAYING; + if (m_callsignAtStart) + sendCallsign(); + } + + beginRelaying(); + + m_callsignTimer.start(); + + m_modemState = STATE_FM; +} + +void CFM::kerchunkState(bool validSignal) +{ + if (validSignal) { + if (m_kerchunkTimer.hasExpired()) { + m_state = FS_RELAYING; + m_kerchunkTimer.stop(); + } + } else { + m_state = FS_LISTENING; + m_kerchunkTimer.stop(); + m_timeoutTimer.stop(); + m_ackMinTimer.stop(); + m_callsignTimer.stop(); + m_holdoffTimer.stop(); + m_modemState = STATE_IDLE; + } +} + +void CFM::relayingState(bool validSignal) +{ + if (validSignal) { + if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { + m_state = FS_TIMEOUT; + m_ackMinTimer.stop(); + m_timeoutTimer.stop(); + m_timeoutTone.start(); + } + } else { + m_state = FS_RELAYING_WAIT; + m_ackDelayTimer.start(); + } + + if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) { + sendCallsign(); + m_callsignTimer.start(); + } +} + +void CFM::relayingWaitState(bool validSignal) +{ + if (validSignal) { + m_state = FS_RELAYING; + m_ackDelayTimer.stop(); + } else { + if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) { + m_state = FS_HANG; + + if (m_ackMinTimer.isRunning()) { + if (m_ackMinTimer.hasExpired()) { + m_rfAck.start(); + m_ackMinTimer.stop(); + } + } else { + m_rfAck.start(); + m_ackMinTimer.stop(); + } + + m_ackDelayTimer.stop(); + m_timeoutTimer.stop(); + m_hangTimer.start(); + } + } + + if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) { + sendCallsign(); + m_callsignTimer.start(); + } +} + +void CFM::hangState(bool validSignal) +{ + if (validSignal) { + m_state = FS_RELAYING; + m_rfAck.stop(); + beginRelaying(); + } else { + if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) { + m_state = FS_LISTENING; + m_hangTimer.stop(); + + if (m_callsignAtEnd) + sendCallsign(); + + m_callsignTimer.stop(); + m_holdoffTimer.stop(); + m_modemState = STATE_IDLE; + } + } + + if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) { + sendCallsign(); + m_callsignTimer.start(); + } +} + +void CFM::timeoutState(bool validSignal) +{ + if (!validSignal) { + m_state = FS_TIMEOUT_WAIT; + m_ackDelayTimer.start(); + } + + if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) { + sendCallsign(); + m_callsignTimer.start(); + } +} + +void CFM::timeoutWaitState(bool validSignal) +{ + if (validSignal) { + m_state = FS_TIMEOUT; + m_ackDelayTimer.stop(); + } else { + if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) { + m_state = FS_HANG; + m_timeoutTone.stop(); + m_rfAck.start(); + m_ackDelayTimer.stop(); + m_ackMinTimer.stop(); + m_timeoutTimer.stop(); + m_hangTimer.start(); + } + } + + if (m_callsignTimer.isRunning() && m_callsignTimer.hasExpired()) { + sendCallsign(); + m_callsignTimer.start(); + } +} + +void CFM::sendCallsign() +{ + if (m_holdoffTimer.isRunning()) { + if (m_holdoffTimer.hasExpired()) { + m_callsign.start(); + m_holdoffTimer.start(); + } + } else { + m_callsign.start(); + } +} + +void CFM::beginRelaying() +{ + m_timeoutTimer.start(); + m_ackMinTimer.start(); +} diff --git a/FM.h b/FM.h index 2ee6aa8..fdc38a7 100644 --- a/FM.h +++ b/FM.h @@ -30,10 +30,10 @@ enum FM_STATE { FS_LISTENING, FS_KERCHUNK, - FS_RELAYING_RF, - FS_RELAYING_WAIT_RF, - FS_TIMEOUT_RF, - FS_TIMEOUT_WAIT_RF, + FS_RELAYING, + FS_RELAYING_WAIT, + FS_TIMEOUT, + FS_TIMEOUT_WAIT, FS_HANG }; @@ -41,7 +41,7 @@ class CFM { public: CFM(); - void samples(bool cos, const q15_t* samples, uint8_t length); + void samples(bool cos, q15_t* samples, uint8_t length); void process(); @@ -67,6 +67,18 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; + + void stateMachine(bool validSignal); + void listeningState(bool validSignal); + void kerchunkState(bool validSignal); + void relayingState(bool validSignal); + void relayingWaitState(bool validSignal); + void timeoutState(bool validSignal); + void timeoutWaitState(bool validSignal); + void hangState(bool validSignal); + + void sendCallsign(); + void beginRelaying(); }; #endif diff --git a/FMTimeout.cpp b/FMTimeout.cpp index 06cf1ee..c0bcea0 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -21,7 +21,8 @@ #include "FMTimeout.h" CFMTimeout::CFMTimeout() : -m_level(128 * 128) +m_level(128 * 128), +m_running(false) { } @@ -33,3 +34,13 @@ void CFMTimeout::setParams(uint8_t level) void CFMTimeout::getAudio(q15_t* samples, uint8_t length) { } + +void CFMTimeout::start() +{ + m_running = true; +} + +void CFMTimeout::stop() +{ + m_running = false; +} diff --git a/FMTimeout.h b/FMTimeout.h index 48c179a..c89b80b 100644 --- a/FMTimeout.h +++ b/FMTimeout.h @@ -27,10 +27,14 @@ public: void setParams(uint8_t level); + void start(); + void stop(); + void getAudio(q15_t* samples, uint8_t length); private: q15_t m_level; + bool m_running; }; #endif diff --git a/FMTimer.cpp b/FMTimer.cpp index b839db4..4711b8f 100644 --- a/FMTimer.cpp +++ b/FMTimer.cpp @@ -20,12 +20,19 @@ #include "Globals.h" #include "FMTimer.h" -CFMTimer::CFMTimer() +CFMTimer::CFMTimer() : +m_timeout(0U) { } -void CFMTimer::setTimeout(uint16_t time) +void CFMTimer::setTimeout(uint16_t timeout) { + m_timeout = timeout; +} + +uint16_t CFMTimer::getTimeout() const +{ + return m_timeout; } void CFMTimer::start() diff --git a/FMTimer.h b/FMTimer.h index a8b91c0..1fb0911 100644 --- a/FMTimer.h +++ b/FMTimer.h @@ -25,7 +25,9 @@ class CFMTimer { public: CFMTimer(); - void setTimeout(uint16_t time); + void setTimeout(uint16_t timeout); + + uint16_t getTimeout() const; void start(); @@ -38,6 +40,7 @@ public: bool hasExpired() const; private: + uint16_t m_timeout; }; #endif From a9a985182fbdf3cbbc57d78a202e26c449506041 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 17:18:01 +0100 Subject: [PATCH 011/119] Add the timer class functionality. --- FM.cpp | 36 +++++++++++++++++------------------- FM.h | 4 ++-- FMTimer.cpp | 28 +++++++++++++++++++++------- FMTimer.h | 11 ++++++----- IO.cpp | 6 ++---- SerialPort.cpp | 10 ++-------- 6 files changed, 50 insertions(+), 45 deletions(-) diff --git a/FM.cpp b/FM.cpp index fd0a4f2..367419a 100644 --- a/FM.cpp +++ b/FM.cpp @@ -39,15 +39,13 @@ m_hangTimer() { } -void CFM::samples(bool cos, q15_t* samples, uint8_t length) +void CFM::samples(q15_t* samples, uint8_t length) { // De-emphasis - bool ctcss = m_goertzel.process(samples, length); + bool validSignal = m_goertzel.process(samples, length); - bool validSignal = ctcss && cos; - - stateMachine(validSignal); + stateMachine(validSignal, length); if (m_modemState != STATE_FM) return; @@ -88,15 +86,15 @@ void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, u m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; - m_holdoffTimer.setTimeout(holdoff); + m_holdoffTimer.setTimeout(holdoff, 0U); } void CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) { m_rfAck.setParams(rfAck, speed, frequency, level); - m_ackDelayTimer.setTimeout(delay); - m_ackMinTimer.setTimeout(minTime); + m_ackDelayTimer.setTimeout(0U, delay); + m_ackMinTimer.setTimeout(minTime, 0U); } void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) @@ -105,20 +103,20 @@ void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency m_goertzel.setParams(ctcssFrequency, ctcssThreshold); m_ctcss.setParams(ctcssFrequency, ctcssLevel); - m_timeoutTimer.setTimeout(timeout); - m_kerchunkTimer.setTimeout(kerchunkTime); - m_hangTimer.setTimeout(hangTime); + m_timeoutTimer.setTimeout(timeout, 0U); + m_kerchunkTimer.setTimeout(kerchunkTime, 0U); + m_hangTimer.setTimeout(hangTime, 0U); } -void CFM::stateMachine(bool validSignal) +void CFM::stateMachine(bool validSignal, uint8_t length) { - m_callsignTimer.clock(); - m_timeoutTimer.clock(); - m_holdoffTimer.clock(); - m_kerchunkTimer.clock(); - m_ackMinTimer.clock(); - m_ackDelayTimer.clock(); - m_hangTimer.clock(); + m_callsignTimer.clock(length); + m_timeoutTimer.clock(length); + m_holdoffTimer.clock(length); + m_kerchunkTimer.clock(length); + m_ackMinTimer.clock(length); + m_ackDelayTimer.clock(length); + m_hangTimer.clock(length); switch (m_state) { case FS_LISTENING: diff --git a/FM.h b/FM.h index fdc38a7..eab9450 100644 --- a/FM.h +++ b/FM.h @@ -41,7 +41,7 @@ class CFM { public: CFM(); - void samples(bool cos, q15_t* samples, uint8_t length); + void samples(q15_t* samples, uint8_t length); void process(); @@ -68,7 +68,7 @@ private: CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; - void stateMachine(bool validSignal); + void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); void kerchunkState(bool validSignal); void relayingState(bool validSignal); diff --git a/FMTimer.cpp b/FMTimer.cpp index 4711b8f..826bc13 100644 --- a/FMTimer.cpp +++ b/FMTimer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2009,2010,2015,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 @@ -21,36 +21,50 @@ #include "FMTimer.h" CFMTimer::CFMTimer() : -m_timeout(0U) +m_timeout(0U), +m_timer(0U) { } -void CFMTimer::setTimeout(uint16_t timeout) +void CFMTimer::setTimeout(uint16_t secs, uint32_t msecs) { - m_timeout = timeout; + m_timeout = (secs * 24000U) + (msecs * 24U); } -uint16_t CFMTimer::getTimeout() const +uint32_t CFMTimer::getTimeout() const { - return m_timeout; + return m_timeout / 24U; } void CFMTimer::start() { + if (m_timeout > 0U) + m_timer = 1U; } void CFMTimer::stop() { + m_timer = 0U; } -void CFMTimer::clock() +void CFMTimer::clock(uint8_t length) { + if (m_timer > 0U && m_timeout > 0U) + m_timer += length; } bool CFMTimer::isRunning() const { + return m_timer > 0U; } bool CFMTimer::hasExpired() const { + if (m_timeout == 0U || m_timer == 0U) + return false; + + if (m_timer > m_timeout) + return true; + + return false; } diff --git a/FMTimer.h b/FMTimer.h index 1fb0911..00dc343 100644 --- a/FMTimer.h +++ b/FMTimer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2009,2010,2015,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 @@ -25,22 +25,23 @@ class CFMTimer { public: CFMTimer(); - void setTimeout(uint16_t timeout); + void setTimeout(uint16_t secs, uint32_t msecs); - uint16_t getTimeout() const; + uint32_t getTimeout() const; void start(); void stop(); - void clock(); + void clock(uint8_t length); bool isRunning() const; bool hasExpired() const; private: - uint16_t m_timeout; + uint32_t m_timeout; + uint32_t m_timer; }; #endif diff --git a/IO.cpp b/IO.cpp index 94b0cbb..93a8109 100644 --- a/IO.cpp +++ b/IO.cpp @@ -348,7 +348,6 @@ void CIO::process() } if (m_fmEnable) { - bool cos = getCOSInt(); q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { @@ -361,7 +360,7 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - fm.samples(cos, FMVals, RX_BLOCK_SIZE); + fm.samples(FMVals, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -422,7 +421,6 @@ void CIO::process() nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_FM) { - bool cos = getCOSInt(); q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { @@ -435,7 +433,7 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - fm.samples(cos, FMVals, RX_BLOCK_SIZE); + fm.samples(FMVals, RX_BLOCK_SIZE); } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); diff --git a/SerialPort.cpp b/SerialPort.cpp index e893b0e..0d81a09 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200411 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200415 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -413,7 +413,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 8U) + if (length < 7U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -426,12 +426,6 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t kerchunkTime = data[5U]; uint8_t hangTime = data[6U]; - char ack[50U]; - uint8_t n = 0U; - for (uint8_t i = 7U; i < length; i++, n++) - ack[n] = data[i]; - ack[n] = '\0'; - fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); return 0U; From ebd87584da4254fb65244aad7f7e45013318307b Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 18:38:39 +0100 Subject: [PATCH 012/119] Add the timeout tones. --- FM.cpp | 9 ++++++--- FMKeyer.cpp | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ FMTimeout.cpp | 31 ++++++++++++++++++++++++++++- FMTimeout.h | 6 ++++-- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 367419a..41826e2 100644 --- a/FM.cpp +++ b/FM.cpp @@ -56,11 +56,14 @@ void CFM::samples(q15_t* samples, uint8_t length) samples[i] = 0; } - m_rfAck.getAudio(samples, length); + if (!m_callsign.isRunning()) + m_rfAck.getAudio(samples, length); - m_callsign.getAudio(samples, length); + if (!m_rfAck.isRunning()) + m_callsign.getAudio(samples, length); - m_timeoutTone.getAudio(samples, length); + if (!m_callsign.isRunning() && !m_rfAck.isRunning()) + m_timeoutTone.getAudio(samples, length); // Band-pass filter diff --git a/FMKeyer.cpp b/FMKeyer.cpp index a3eb181..88f3fff 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -20,6 +20,61 @@ #include "Globals.h" #include "FMKeyer.h" +const struct { + uint8_t c; + uint32_t pattern; + uint8_t length; +} SYMBOL_LIST[] = { + {'A', 0xB8000000U, 8U}, + {'B', 0xEA800000U, 12U}, + {'C', 0xEBA00000U, 14U}, + {'D', 0xEA000000U, 10U}, + {'E', 0x80000000U, 4U}, + {'F', 0xAE800000U, 12U}, + {'G', 0xEE800000U, 12U}, + {'H', 0xAA000000U, 10U}, + {'I', 0xA0000000U, 6U}, + {'J', 0xBBB80000U, 16U}, + {'K', 0xEB800000U, 12U}, + {'L', 0xBA800000U, 12U}, + {'M', 0xEE000000U, 10U}, + {'N', 0xE8000000U, 8U}, + {'O', 0xEEE00000U, 14U}, + {'P', 0xBBA00000U, 14U}, + {'Q', 0xEEB80000U, 16U}, + {'R', 0xBA000000U, 10U}, + {'S', 0xA8000000U, 8U}, + {'T', 0xE0000000U, 6U}, + {'U', 0xAE000000U, 10U}, + {'V', 0xAB800000U, 12U}, + {'W', 0xBB800000U, 12U}, + {'X', 0xEAE00000U, 14U}, + {'Y', 0xEBB80000U, 16U}, + {'Z', 0xEEA00000U, 14U}, + {'1', 0xBBBB8000U, 20U}, + {'2', 0xAEEE0000U, 18U}, + {'3', 0xABB80000U, 16U}, + {'4', 0xAAE00000U, 14U}, + {'5', 0xAA800000U, 12U}, + {'6', 0xEAA00000U, 14U}, + {'7', 0xEEA80000U, 16U}, + {'8', 0xEEEA0000U, 18U}, + {'9', 0xEEEE8000U, 20U}, + {'0', 0xEEEEE000U, 22U}, + {'/', 0xEAE80000U, 16U}, + {'?', 0xAEEA0000U, 18U}, + {',', 0xEEAEE000U, 22U}, + {'-', 0xEAAE0000U, 18U}, + {'=', 0xEAB80000U, 16U}, + {' ', 0x00000000U, 4U}, + {0U, 0x00000000U, 0U} +}; + +const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + CFMKeyer::CFMKeyer() : m_level(128 * 128), m_wanted(false), diff --git a/FMTimeout.cpp b/FMTimeout.cpp index c0bcea0..0090f2e 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -20,9 +20,17 @@ #include "Globals.h" #include "FMTimeout.h" +// 400 Hz sine wave at 24000 Hz sample rate +const q15_t BUSY_AUDIO[] = {0, 3426, 6813, 10126, 13328, 16384, 19261, 21926, 24351, 26510, 28378, 29935, 31164, 32052, 32588, 32768, 32588, 32052, 31164, 29935, 28378, 26510, 24351, + 21926, 19261, 16384, 13328, 10126, 6813, 3425, 0, -3425, -6813, -10126, -13328, -16384, -19261, -21926, -24351, -26510, -28378, -29935, -31164, -32052, + -32588, -32768, -32588, -32052, -31164, -29935, -28378, -26510, -24351, -21926, -19261, -16384, -13328, -10126, -6813, -3425}; +const uint8_t BUSY_AUDIO_LEN = 60U; + CFMTimeout::CFMTimeout() : m_level(128 * 128), -m_running(false) +m_running(false), +m_pos(0U), +m_n(0U) { } @@ -33,11 +41,32 @@ void CFMTimeout::setParams(uint8_t level) void CFMTimeout::getAudio(q15_t* samples, uint8_t length) { + if (!m_running) + return; + + for (uint8_t i = 0U; i < length; i++) { + if (m_pos > 12000U) { + q31_t sample = BUSY_AUDIO[m_n] * m_level; + samples[i] = q15_t(__SSAT((sample >> 15), 16)); + + m_n++; + if (m_n >= BUSY_AUDIO_LEN) + m_n = 0U; + } else { + samples[i] = 0; + } + + m_pos++; + if (m_pos >= 24000U) + m_pos = 0U; + } } void CFMTimeout::start() { m_running = true; + m_pos = 0U; + m_n = 0U; } void CFMTimeout::stop() diff --git a/FMTimeout.h b/FMTimeout.h index c89b80b..6286ddb 100644 --- a/FMTimeout.h +++ b/FMTimeout.h @@ -33,8 +33,10 @@ public: void getAudio(q15_t* samples, uint8_t length); private: - q15_t m_level; - bool m_running; + q15_t m_level; + bool m_running; + uint32_t m_pos; + uint8_t m_n; }; #endif From c103ac91b370d7d64f06fe1162c4ed5cc5fa21e8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 19:49:26 +0100 Subject: [PATCH 013/119] Add the CTCSS tone generator. --- FMCTCSSTX.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++- FMCTCSSTX.h | 10 ++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 8c98856..0d8982a 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -20,16 +20,90 @@ #include "Globals.h" #include "FMCTCSSTX.h" +const CTCSS_TABLE CTCSS_TONES[] = { + {67U, 358U, {0, 575, 1149, 1724, 2297, 2870, 3442, 4013, 4583, 5151, 5718, 6283, 6846, 7407, 7966, 8522, 9076, 9627, 10175, 10720, 11261, 11799, 12333, 12864, 13391, 13913, 14431, 14945, 15454, 15959, 16458, 16953, 17442, 17926, 18404, 18877, 19344, 19805, 20260, 20708, 21150, 21586, 22015, 22437, 22853, 23261, 23663, 24056, 24443, 24822, 25193, 25557, 25913, 26261, 26600, 26932, 27255, 27570, 27876, 28174, 28463, 28744, 29015, 29278, 29531, 29776, 30011, 30237, 30454, 30662, 30860, 31048, 31227, 31397, 31556, 31706, 31847, 31977, 32098, 32208, 32309, 32400, 32481, 32552, 32613, 32664, 32704, 32735, 32756, 32766, 32767, 32757, 32738, 32708, 32668, 32619, 32559, 32489, 32409, 32319, 32219, 32110, 31990, 31861, 31721, 31572, 31414, 31245, 31067, 30880, 30683, 30476, 30261, 30035, 29801, 29557, 29305, 29043, 28772, 28493, 28205, 27908, 27602, 27288, 26966, 26635, 26296, 25950, 25595, 25232, 24861, 24483, 24097, 23704, 23304, 22896, 22481, 22060, 21631, 21196, 20755, 20307, 19852, 19392, 18926, 18454, 17976, 17493, 17004, 16510, 16011, 15507, 14999, 14485, 13967, 13445, 12919, 12389, 11855, 11317, 10776, 10232, 9684, 9134, 8580, 8024, 7466, 6905, 6342, 5777, 5211, 4643, 4073, 3502, 2930, 2357, 1783, 1209, 635, 60, -514, -1088, -1663, -2236, -2809, -3382, -3953, -4523, -5091, -5658, -6223, -6787, -7348, -7907, -8463, -9017, -9568, -10117, -10662, -11204, -11742, -12277, -12808, -13335, -13858, -14376, -14891, -15400, -15905, -16405, -16900, -17390, -17875, -18353, -18827, -19294, -19756, -20211, -20661, -21103, -21540, -21970, -22393, -22809, -23218, -23620, -24015, -24402, -24782, -25154, -25518, -25875, -26224, -26564, -26897, -27221, -27536, -27844, -28142, -28433, -28714, -28986, -29250, -29504, -29750, -29986, -30213, -30431, -30639, -30838, -31028, -31208, -31378, -31539, -31690, -31831, -31963, -32084, -32196, -32298, -32390, -32472, -32544, -32606, -32658, -32700, -32731, -32753, -32765, -32766, -32758, -32739, -32711, -32672, -32623, -32564, -32496, -32417, -32328, -32229, -32121, -32002, -31874, -31735, -31587, -31430, -31262, -31085, -30899, -30703, -30497, -30283, -30058, -29825, -29582, -29331, -29070, -28800, -28522, -28234, -27938, -27634, -27321, -26999, -26669, -26331, -25985, -25631, -25269, -24899, -24522, -24137, -23744, -23345, -22938, -22524, -22103, -21675, -21241, -20800, -20353, -19899, -19440, -18974, -18502, -18025, -17542, -17054, -16561, -16063, -15559, -15051, -14538, -14021, -13499, -12973, -12444, -11910, -11373, -10832, -10288, -9741, -9190, -8637, -8082, -7523, -6963, -6400, -5835, -5269, -4701, -4131, -3561, -2989, -2416, -1842, -1268, -694}}, + {69U, 346U, {0, 594, 1189, 1783, 2376, 2968, 3560, 4150, 4739, 5327, 5912, 6496, 7078, 7657, 8234, 8808, 9379, 9947, 10512, 11073, 11631, 12185, 12734, 13280, 13821, 14358, 14890, 15417, 15939, 16456, 16967, 17473, 17973, 18467, 18955, 19437, 19912, 20381, 20843, 21299, 21747, 22188, 22622, 23048, 23467, 23878, 24281, 24676, 25063, 25442, 25813, 26175, 26528, 26873, 27208, 27535, 27853, 28161, 28461, 28751, 29031, 29302, 29563, 29815, 30057, 30288, 30510, 30722, 30924, 31115, 31297, 31468, 31628, 31778, 31918, 32047, 32166, 32274, 32372, 32459, 32535, 32600, 32655, 32699, 32732, 32755, 32766, 32767, 32757, 32736, 32705, 32663, 32610, 32546, 32472, 32386, 32291, 32184, 32067, 31940, 31802, 31653, 31494, 31325, 31145, 30955, 30755, 30545, 30325, 30094, 29854, 29604, 29345, 29075, 28796, 28508, 28210, 27903, 27587, 27261, 26927, 26584, 26232, 25871, 25502, 25125, 24739, 24345, 23943, 23533, 23116, 22691, 22258, 21818, 21371, 20917, 20456, 19988, 19514, 19033, 18546, 18053, 17554, 17049, 16538, 16022, 15501, 14975, 14444, 13908, 13367, 12822, 12273, 11720, 11163, 10602, 10038, 9470, 8900, 8326, 7750, 7171, 6589, 6006, 5421, 4834, 4245, 3655, 3063, 2471, 1878, 1284, 690, 95, -498, -1093, -1687, -2280, -2873, -3464, -4055, -4644, -5232, -5818, -6402, -6984, -7563, -8141, -8715, -9287, -9855, -10421, -10982, -11541, -12095, -12646, -13192, -13734, -14271, -14804, -15332, -15855, -16373, -16885, -17392, -17892, -18388, -18877, -19359, -19836, -20306, -20769, -21225, -21675, -22117, -22552, -22979, -23399, -23812, -24216, -24613, -25001, -25381, -25753, -26116, -26471, -26817, -27154, -27482, -27802, -28112, -28412, -28704, -28986, -29258, -29521, -29774, -30018, -30251, -30474, -30688, -30891, -31084, -31267, -31440, -31602, -31754, -31896, -32026, -32147, -32257, -32356, -32444, -32522, -32589, -32646, -32692, -32727, -32751, -32764, -32767, -32758, -32739, -32710, -32669, -32618, -32556, -32483, -32400, -32306, -32201, -32086, -31960, -31823, -31677, -31519, -31352, -31174, -30985, -30787, -30578, -30360, -30131, -29892, -29644, -29386, -29118, -28841, -28554, -28257, -27952, -27637, -27313, -26980, -26638, -26288, -25929, -25561, -25185, -24800, -24408, -24007, -23599, -23182, -22758, -22327, -21888, -21442, -20989, -20529, -20062, -19589, -19109, -18623, -18131, -17633, -17129, -16619, -16104, -15584, -15059, -14528, -13993, -13453, -12909, -12360, -11808, -11251, -10691, -10127, -9560, -8990, -8417, -7841, -7263, -6682, -6099, -5514, -4927, -4338, -3748, -3157, -2565, -1972, -1378, -784}}, + {71U, 334U, {0, 617, 1233, 1849, 2465, 3079, 3693, 4305, 4916, 5525, 6132, 6736, 7339, 7939, 8536, 9130, 9720, 10308, 10891, 11471, 12047, 12618, 13185, 13747, 14305, 14857, 15404, 15946, 16482, 17012, 17536, 18054, 18566, 19071, 19569, 20060, 20544, 21021, 21490, 21952, 22406, 22852, 23290, 23720, 24141, 24554, 24958, 25353, 25740, 26117, 26485, 26843, 27192, 27531, 27861, 28181, 28490, 28790, 29080, 29359, 29627, 29886, 30133, 30370, 30596, 30812, 31016, 31210, 31392, 31563, 31724, 31872, 32010, 32136, 32251, 32354, 32446, 32527, 32596, 32653, 32699, 32733, 32756, 32767, 32766, 32754, 32730, 32695, 32648, 32589, 32519, 32438, 32344, 32240, 32124, 31997, 31858, 31708, 31547, 31374, 31191, 30996, 30790, 30574, 30347, 30109, 29860, 29601, 29331, 29051, 28760, 28459, 28149, 27828, 27497, 27157, 26807, 26448, 26079, 25701, 25314, 24918, 24513, 24099, 23677, 23246, 22807, 22360, 21906, 21443, 20973, 20495, 20010, 19518, 19020, 18514, 18002, 17483, 16959, 16428, 15891, 15349, 14801, 14249, 13691, 13128, 12560, 11988, 11412, 10832, 10248, 9661, 9069, 8475, 7878, 7278, 6675, 6070, 5463, 4854, 4243, 3631, 3017, 2402, 1787, 1171, 554, -62, -678, -1295, -1911, -2526, -3141, -3754, -4366, -4977, -5585, -6192, -6797, -7399, -7998, -8595, -9189, -9779, -10366, -10949, -11529, -12104, -12675, -13241, -13803, -14360, -14912, -15459, -16000, -16535, -17065, -17588, -18105, -18616, -19120, -19618, -20108, -20592, -21068, -21537, -21998, -22451, -22896, -23333, -23762, -24183, -24595, -24998, -25392, -25777, -26154, -26520, -26878, -27226, -27564, -27893, -28212, -28520, -28819, -29107, -29385, -29653, -29910, -30157, -30393, -30618, -30832, -31035, -31228, -31409, -31579, -31738, -31886, -32022, -32147, -32261, -32363, -32454, -32533, -32601, -32657, -32702, -32735, -32756, -32766, -32765, -32751, -32726, -32690, -32641, -32582, -32510, -32428, -32333, -32228, -32110, -31982, -31842, -31691, -31529, -31355, -31170, -30975, -30768, -30550, -30322, -30083, -29833, -29573, -29302, -29021, -28729, -28427, -28116, -27794, -27462, -27121, -26770, -26410, -26040, -25661, -25273, -24876, -24470, -24055, -23632, -23201, -22761, -22314, -21858, -21395, -20924, -20445, -19960, -19467, -18968, -18461, -17948, -17429, -16904, -16373, -15835, -15293, -14745, -14191, -13633, -13069, -12502, -11929, -11353, -10772, -10188, -9600, -9008, -8414, -7816, -7216, -6613, -6008, -5400, -4791, -4180, -3567, -2954, -2339, -1723, -1107, -491}}, + {74U, 323U, {0, 638, 1276, 1914, 2550, 3186, 3821, 4454, 5085, 5715, 6342, 6967, 7589, 8209, 8825, 9438, 10048, 10653, 11255, 11852, 12445, 13033, 13616, 14194, 14766, 15333, 15894, 16449, 16998, 17541, 18076, 18605, 19127, 19642, 20149, 20648, 21140, 21623, 22099, 22566, 23024, 23474, 23915, 24347, 24769, 25182, 25586, 25980, 26364, 26738, 27102, 27455, 27799, 28131, 28453, 28764, 29065, 29354, 29632, 29899, 30154, 30398, 30631, 30852, 31061, 31258, 31444, 31617, 31779, 31929, 32066, 32191, 32305, 32405, 32494, 32570, 32634, 32685, 32725, 32751, 32765, 32767, 32757, 32734, 32698, 32650, 32590, 32517, 32432, 32335, 32226, 32104, 31970, 31824, 31666, 31495, 31313, 31119, 30914, 30696, 30467, 30226, 29974, 29710, 29436, 29150, 28853, 28545, 28226, 27896, 27556, 27205, 26845, 26474, 26092, 25701, 25301, 24890, 24470, 24041, 23603, 23156, 22700, 22235, 21762, 21281, 20792, 20295, 19790, 19277, 18757, 18231, 17697, 17156, 16609, 16056, 15497, 14931, 14360, 13784, 13202, 12616, 12024, 11428, 10828, 10224, 9615, 9003, 8388, 7770, 7148, 6524, 5897, 5268, 4637, 4005, 3371, 2735, 2099, 1461, 823, 185, -452, -1090, -1728, -2365, -3001, -3636, -4269, -4901, -5531, -6159, -6785, -7408, -8028, -8646, -9260, -9870, -10477, -11079, -11678, -12272, -12861, -13446, -14025, -14600, -15168, -15731, -16288, -16838, -17383, -17920, -18451, -18975, -19492, -20001, -20503, -20997, -21483, -21961, -22430, -22891, -23344, -23787, -24221, -24647, -25062, -25469, -25866, -26252, -26629, -26996, -27353, -27699, -28035, -28360, -28674, -28977, -29270, -29551, -29821, -30080, -30328, -30563, -30788, -31000, -31201, -31390, -31567, -31732, -31885, -32026, -32155, -32272, -32376, -32468, -32548, -32616, -32671, -32713, -32744, -32762, -32767, -32760, -32741, -32709, -32664, -32608, -32539, -32457, -32364, -32258, -32139, -32009, -31866, -31712, -31545, -31366, -31176, -30974, -30759, -30534, -30296, -30047, -29787, -29516, -29233, -28939, -28634, -28318, -27992, -27655, -27307, -26949, -26581, -26203, -25815, -25417, -25009, -24592, -24166, -23730, -23286, -22832, -22370, -21900, -21421, -20934, -20439, -19936, -19426, -18908, -18383, -17851, -17313, -16768, -16216, -15659, -15095, -14526, -13951, -13371, -12786, -12196, -11601, -11002, -10399, -9791, -9181, -8566, -7948, -7328, -6704, -6078, -5450, -4820, -4188, -3554, -2919, -2282, -1645, -1008, -370}}, + {77U, 312U, {0, 661, 1321, 1980, 2639, 3297, 3954, 4609, 5262, 5912, 6561, 7207, 7850, 8489, 9126, 9758, 10387, 11011, 11631, 12246, 12856, 13461, 14061, 14654, 15242, 15824, 16399, 16967, 17529, 18083, 18631, 19170, 19702, 20226, 20741, 21248, 21747, 22237, 22717, 23189, 23651, 24103, 24546, 24978, 25401, 25813, 26214, 26605, 26986, 27355, 27713, 28060, 28395, 28719, 29031, 29332, 29620, 29897, 30161, 30413, 30653, 30880, 31095, 31297, 31486, 31663, 31826, 31977, 32115, 32239, 32351, 32450, 32535, 32607, 32666, 32711, 32743, 32762, 32768, 32760, 32739, 32705, 32657, 32596, 32522, 32435, 32335, 32221, 32094, 31954, 31802, 31636, 31457, 31266, 31062, 30845, 30616, 30374, 30120, 29854, 29576, 29286, 28983, 28669, 28344, 28006, 27658, 27298, 26927, 26545, 26152, 25749, 25335, 24911, 24477, 24033, 23579, 23116, 22643, 22161, 21670, 21170, 20662, 20145, 19620, 19087, 18546, 17998, 17442, 16879, 16310, 15733, 15151, 14562, 13967, 13367, 12761, 12150, 11535, 10914, 10289, 9660, 9027, 8390, 7750, 7106, 6460, 5811, 5160, 4507, 3851, 3195, 2537, 1878, 1218, 558, -102, -762, -1423, -2082, -2741, -3399, -4055, -4709, -5362, -6013, -6661, -7306, -7948, -8588, -9223, -9855, -10483, -11107, -11726, -12340, -12950, -13554, -14152, -14745, -15332, -15913, -16487, -17054, -17615, -18168, -18714, -19253, -19783, -20306, -20820, -21326, -21823, -22311, -22790, -23260, -23721, -24172, -24613, -25044, -25465, -25875, -26275, -26664, -27043, -27410, -27767, -28112, -28445, -28767, -29078, -29376, -29663, -29938, -30200, -30450, -30688, -30913, -31126, -31326, -31513, -31688, -31850, -31998, -32134, -32257, -32366, -32463, -32546, -32616, -32673, -32716, -32746, -32763, -32767, -32757, -32734, -32697, -32648, -32585, -32509, -32419, -32317, -32201, -32072, -31930, -31776, -31608, -31427, -31234, -31028, -30809, -30578, -30335, -30079, -29811, -29530, -29238, -28934, -28618, -28291, -27952, -27601, -27240, -26867, -26484, -26089, -25684, -25269, -24843, -24408, -23962, -23507, -23042, -22567, -22084, -21592, -21090, -20581, -20062, -19536, -19002, -18460, -17910, -17354, -16790, -16219, -15642, -15059, -14469, -13873, -13272, -12665, -12054, -11437, -10816, -10190, -9560, -8927, -8289, -7649, -7005, -6358, -5709, -5057, -4404, -3748, -3091, -2433, -1774, -1114, -454}}, + {79U, 301U, {0, 684, 1367, 2050, 2732, 3412, 4092, 4769, 5444, 6117, 6788, 7455, 8119, 8780, 9436, 10089, 10737, 11381, 12020, 12653, 13281, 13903, 14519, 15129, 15732, 16328, 16917, 17499, 18073, 18640, 19198, 19748, 20289, 20822, 21345, 21859, 22364, 22858, 23343, 23818, 24282, 24736, 25179, 25611, 26032, 26442, 26840, 27226, 27601, 27963, 28313, 28651, 28977, 29290, 29590, 29877, 30151, 30413, 30661, 30895, 31116, 31324, 31518, 31698, 31864, 32017, 32155, 32280, 32390, 32487, 32569, 32637, 32691, 32731, 32756, 32767, 32764, 32747, 32715, 32669, 32609, 32535, 32447, 32344, 32227, 32097, 31952, 31793, 31621, 31435, 31235, 31022, 30795, 30554, 30301, 30034, 29754, 29461, 29155, 28837, 28506, 28162, 27807, 27439, 27059, 26668, 26265, 25850, 25424, 24988, 24540, 24081, 23613, 23133, 22644, 22145, 21636, 21118, 20591, 20055, 19510, 18956, 18394, 17824, 17247, 16662, 16069, 15470, 14864, 14252, 13633, 13008, 12378, 11742, 11101, 10456, 9806, 9151, 8493, 7830, 7165, 6496, 5825, 5151, 4474, 3796, 3116, 2435, 1753, 1070, 386, -297, -980, -1663, -2346, -3027, -3707, -4386, -5062, -5737, -6409, -7078, -7744, -8406, -9065, -9720, -10371, -11017, -11659, -12295, -12926, -13551, -14171, -14784, -15391, -15991, -16585, -17171, -17749, -18320, -18883, -19437, -19984, -20521, -21050, -21569, -22079, -22579, -23070, -23550, -24020, -24480, -24929, -25368, -25795, -26211, -26615, -27008, -27390, -27759, -28116, -28461, -28794, -29114, -29421, -29716, -29997, -30266, -30521, -30763, -30992, -31207, -31409, -31597, -31771, -31931, -32078, -32210, -32329, -32433, -32523, -32600, -32661, -32709, -32743, -32762, -32767, -32758, -32734, -32696, -32644, -32578, -32497, -32403, -32294, -32171, -32034, -31884, -31719, -31541, -31349, -31143, -30923, -30691, -30444, -30185, -29912, -29627, -29328, -29017, -28693, -28357, -28008, -27647, -27274, -26889, -26493, -26085, -25665, -25235, -24793, -24341, -23878, -23404, -22921, -22427, -21924, -21411, -20889, -20357, -19817, -19269, -18711, -18146, -17573, -16992, -16404, -15808, -15206, -14597, -13982, -13361, -12733, -12101, -11463, -10820, -10172, -9520, -8864, -8204, -7540, -6873, -6203, -5530, -4855, -4178, -3499, -2819, -2137, -1454, -771}}, + {82U, 291U, {0, 708, 1415, 2122, 2827, 3532, 4235, 4935, 5634, 6330, 7022, 7712, 8398, 9080, 9758, 10431, 11100, 11763, 12421, 13073, 13719, 14358, 14991, 15617, 16235, 16846, 17449, 18044, 18631, 19208, 19777, 20337, 20887, 21428, 21958, 22478, 22988, 23487, 23975, 24452, 24917, 25371, 25813, 26243, 26660, 27066, 27458, 27838, 28205, 28558, 28899, 29226, 29539, 29838, 30124, 30395, 30653, 30896, 31124, 31338, 31538, 31722, 31892, 32047, 32188, 32313, 32423, 32518, 32597, 32662, 32711, 32745, 32764, 32767, 32756, 32729, 32686, 32629, 32556, 32468, 32365, 32246, 32113, 31965, 31802, 31624, 31431, 31223, 31001, 30765, 30514, 30249, 29970, 29677, 29370, 29049, 28715, 28367, 28006, 27632, 27246, 26846, 26434, 26010, 25573, 25125, 24665, 24193, 23710, 23216, 22711, 22196, 21670, 21134, 20588, 20033, 19468, 18894, 18312, 17721, 17121, 16514, 15899, 15276, 14647, 14010, 13367, 12718, 12063, 11402, 10736, 10065, 9389, 8709, 8024, 7336, 6645, 5950, 5253, 4553, 3851, 3148, 2443, 1736, 1029, 322, -385, -1093, -1800, -2506, -3211, -3914, -4616, -5316, -6013, -6707, -7398, -8086, -8770, -9449, -10125, -10796, -11461, -12122, -12776, -13425, -14067, -14703, -15332, -15954, -16568, -17175, -17774, -18364, -18946, -19519, -20083, -20637, -21182, -21717, -22242, -22756, -23260, -23753, -24235, -24706, -25165, -25612, -26048, -26471, -26882, -27280, -27666, -28039, -28398, -28745, -29078, -29397, -29703, -29995, -30273, -30536, -30786, -31021, -31242, -31448, -31639, -31816, -31978, -32125, -32257, -32374, -32475, -32562, -32633, -32690, -32731, -32756, -32767, -32762, -32742, -32706, -32656, -32590, -32509, -32412, -32301, -32174, -32033, -31877, -31705, -31519, -31318, -31103, -30873, -30629, -30370, -30097, -29811, -29510, -29196, -28867, -28526, -28171, -27803, -27422, -27028, -26622, -26203, -25772, -25329, -24874, -24408, -23930, -23441, -22941, -22430, -21909, -21378, -20836, -20285, -19725, -19155, -18577, -17989, -17394, -16790, -16178, -15559, -14933, -14299, -13659, -13013, -12360, -11702, -11038, -10369, -9696, -9017, -8335, -7649, -6959, -6265, -5569, -4871, -4170, -3467, -2762, -2057, -1350, -642}}, + {85U, 281U, {0, 733, 1465, 2196, 2927, 3655, 4383, 5107, 5830, 6549, 7265, 7978, 8686, 9390, 10090, 10784, 11473, 12157, 12834, 13505, 14169, 14826, 15475, 16117, 16751, 17377, 17993, 18601, 19199, 19788, 20367, 20936, 21494, 22042, 22578, 23104, 23617, 24119, 24609, 25087, 25552, 26004, 26443, 26869, 27282, 27681, 28066, 28437, 28794, 29136, 29464, 29777, 30076, 30359, 30627, 30880, 31117, 31339, 31545, 31736, 31910, 32069, 32211, 32338, 32448, 32542, 32620, 32681, 32726, 32755, 32767, 32763, 32743, 32706, 32653, 32584, 32498, 32396, 32278, 32144, 31994, 31827, 31645, 31447, 31233, 31004, 30759, 30499, 30224, 29933, 29627, 29307, 28972, 28623, 28259, 27881, 27489, 27083, 26664, 26232, 25786, 25328, 24857, 24373, 23877, 23370, 22850, 22320, 21778, 21225, 20662, 20088, 19504, 18910, 18308, 17695, 17074, 16445, 15807, 15162, 14508, 13848, 13180, 12506, 11826, 11140, 10448, 9751, 9050, 8343, 7633, 6919, 6201, 5480, 4756, 4030, 3302, 2573, 1842, 1110, 377, -354, -1087, -1818, -2549, -3279, -4007, -4733, -5457, -6178, -6896, -7610, -8321, -9027, -9729, -10426, -11118, -11804, -12485, -13159, -13827, -14487, -15141, -15787, -16425, -17054, -17676, -18288, -18891, -19485, -20069, -20643, -21207, -21760, -22302, -22833, -23353, -23861, -24357, -24841, -25313, -25772, -26217, -26650, -27070, -27476, -27868, -28246, -28611, -28961, -29296, -29617, -29923, -30214, -30490, -30751, -30996, -31226, -31440, -31638, -31821, -31988, -32139, -32273, -32392, -32494, -32581, -32650, -32704, -32741, -32762, -32767, -32755, -32726, -32682, -32621, -32543, -32450, -32340, -32214, -32072, -31914, -31740, -31550, -31345, -31123, -30886, -30634, -30366, -30084, -29786, -29473, -29145, -28803, -28447, -28076, -27692, -27293, -26881, -26455, -26016, -25565, -25100, -24623, -24133, -23632, -23118, -22594, -22057, -21510, -20952, -20384, -19805, -19216, -18618, -18011, -17394, -16769, -16136, -15494, -14845, -14188, -13524, -12854, -12176, -11493, -10804, -10110, -9411, -8707, -7998, -7286, -6570, -5851, -5128, -4404, -3677, -2948, -2217, -1486, -754}}, + {88U, 271U, {0, 759, 1518, 2276, 3032, 3788, 4541, 5291, 6039, 6783, 7524, 8261, 8994, 9721, 10444, 11160, 11871, 12575, 13273, 13964, 14647, 15322, 15989, 16647, 17296, 17937, 18567, 19188, 19798, 20397, 20986, 21563, 22129, 22683, 23225, 23754, 24271, 24774, 25265, 25741, 26204, 26653, 27087, 27507, 27912, 28303, 28678, 29037, 29381, 29709, 30022, 30318, 30598, 30861, 31108, 31338, 31552, 31748, 31928, 32090, 32235, 32363, 32473, 32566, 32641, 32699, 32740, 32762, 32768, 32755, 32725, 32678, 32613, 32530, 32430, 32313, 32178, 32026, 31857, 31670, 31467, 31247, 31010, 30756, 30486, 30199, 29897, 29578, 29243, 28893, 28527, 28146, 27749, 27338, 26912, 26472, 26017, 25549, 25067, 24571, 24062, 23541, 23006, 22459, 21901, 21330, 20748, 20155, 19551, 18936, 18312, 17677, 17033, 16380, 15718, 15048, 14370, 13684, 12990, 12290, 11583, 10869, 10150, 9426, 8696, 7962, 7223, 6481, 5735, 4986, 4235, 3481, 2725, 1968, 1209, 450, -308, -1067, -1825, -2583, -3339, -4093, -4845, -5595, -6341, -7084, -7824, -8559, -9289, -10015, -10735, -11449, -12157, -12859, -13554, -14241, -14921, -15593, -16257, -16911, -17557, -18193, -18820, -19436, -20042, -20637, -21221, -21794, -22355, -22904, -23441, -23965, -24476, -24974, -25459, -25930, -26387, -26830, -27259, -27673, -28072, -28456, -28825, -29178, -29516, -29837, -30143, -30433, -30706, -30963, -31203, -31426, -31633, -31822, -31995, -32150, -32288, -32409, -32512, -32598, -32666, -32717, -32750, -32766, -32764, -32744, -32707, -32652, -32580, -32491, -32383, -32259, -32117, -31958, -31782, -31589, -31378, -31151, -30907, -30647, -30370, -30077, -29768, -29443, -29101, -28745, -28373, -27985, -27583, -27166, -26734, -26288, -25828, -25353, -24866, -24365, -23851, -23324, -22784, -22232, -21669, -21094, -20507, -19909, -19301, -18683, -18054, -17416, -16768, -16111, -15446, -14772, -14090, -13401, -12705, -12002, -11292, -10577, -9855, -9129, -8397, -7661, -6921, -6177, -5430, -4680, -3927, -3172, -2416, -1658, -900}}, + {91U, 262U, {0, 785, 1569, 2353, 3135, 3915, 4693, 5469, 6241, 7010, 7775, 8535, 9290, 10040, 10784, 11522, 12254, 12978, 13695, 14404, 15105, 15797, 16480, 17154, 17818, 18472, 19115, 19747, 20367, 20976, 21573, 22158, 22730, 23288, 23834, 24366, 24884, 25387, 25876, 26350, 26809, 27253, 27681, 28093, 28489, 28868, 29231, 29578, 29907, 30219, 30514, 30791, 31051, 31293, 31517, 31722, 31910, 32079, 32230, 32363, 32476, 32572, 32648, 32706, 32745, 32765, 32766, 32749, 32713, 32658, 32584, 32492, 32381, 32251, 32103, 31936, 31751, 31548, 31327, 31088, 30831, 30556, 30264, 29954, 29627, 29284, 28923, 28546, 28152, 27743, 27317, 26876, 26419, 25947, 25460, 24959, 24443, 23913, 23370, 22813, 22243, 21660, 21065, 20458, 19839, 19208, 18567, 17915, 17253, 16580, 15899, 15208, 14508, 13800, 13085, 12361, 11631, 10894, 10150, 9401, 8647, 7887, 7123, 6355, 5583, 4808, 4030, 3250, 2468, 1685, 901, 116, -668, -1453, -2236, -3019, -3799, -4578, -5354, -6126, -6896, -7661, -8422, -9178, -9929, -10674, -11413, -12145, -12871, -13589, -14299, -15001, -15695, -16379, -17054, -17720, -18375, -19019, -19653, -20275, -20886, -21485, -22071, -22645, -23206, -23753, -24287, -24807, -25313, -25804, -26280, -26741, -27187, -27618, -28032, -28430, -28812, -29178, -29527, -29859, -30173, -30471, -30751, -31013, -31257, -31484, -31692, -31883, -32055, -32208, -32343, -32460, -32558, -32637, -32697, -32739, -32762, -32766, -32752, -32718, -32666, -32595, -32505, -32397, -32270, -32125, -31961, -31779, -31578, -31360, -31123, -30869, -30597, -30307, -30000, -29676, -29334, -28976, -28602, -28210, -27803, -27380, -26941, -26486, -26016, -25532, -25033, -24519, -23991, -23450, -22895, -22327, -21746, -21153, -20547, -19930, -19301, -18661, -18011, -17350, -16679, -15999, -15309, -14611, -13904, -13190, -12467, -11738, -11002, -10259, -9511, -8757, -7998, -7235, -6467, -5696, -4922, -4144, -3364, -2583, -1800, -1015}}, + {94U, 253U, {0, 813, 1626, 2438, 3248, 4056, 4862, 5664, 6463, 7259, 8049, 8835, 9615, 10390, 11158, 11919, 12673, 13419, 14157, 14886, 15605, 16316, 17016, 17705, 18384, 19052, 19707, 20351, 20982, 21600, 22205, 22796, 23373, 23936, 24484, 25017, 25534, 26036, 26522, 26991, 27444, 27880, 28299, 28700, 29084, 29449, 29797, 30126, 30437, 30728, 31001, 31255, 31490, 31705, 31901, 32077, 32233, 32369, 32486, 32582, 32659, 32715, 32751, 32767, 32763, 32739, 32694, 32629, 32544, 32440, 32315, 32170, 32006, 31821, 31617, 31394, 31151, 30890, 30609, 30309, 29991, 29654, 29299, 28925, 28534, 28126, 27700, 27257, 26797, 26321, 25829, 25320, 24796, 24257, 23703, 23134, 22551, 21954, 21344, 20720, 20084, 19435, 18774, 18102, 17419, 16725, 16020, 15306, 14582, 13849, 13108, 12359, 11602, 10838, 10067, 9290, 8507, 7720, 6927, 6130, 5329, 4525, 3719, 2909, 2099, 1286, 474, -339, -1152, -1964, -2775, -3585, -4392, -5196, -5997, -6795, -7588, -8377, -9161, -9939, -10710, -11476, -12234, -12984, -13727, -14461, -15186, -15902, -16608, -17304, -17989, -18663, -19326, -19977, -20615, -21241, -21854, -22453, -23038, -23609, -24166, -24708, -25234, -25745, -26240, -26719, -27182, -27627, -28056, -28467, -28861, -29237, -29596, -29935, -30257, -30560, -30844, -31109, -31355, -31581, -31788, -31976, -32143, -32291, -32419, -32528, -32616, -32684, -32732, -32759, -32767, -32754, -32721, -32668, -32595, -32502, -32389, -32256, -32103, -31930, -31738, -31526, -31294, -31043, -30774, -30485, -30177, -29851, -29507, -29144, -28763, -28365, -27949, -27516, -27066, -26599, -26116, -25617, -25102, -24572, -24026, -23466, -22891, -22302, -21700, -21084, -20455, -19813, -19159, -18494, -17817, -17129, -16431, -15722, -15004, -14276, -13540, -12795, -12043, -11283, -10516, -9742, -8963, -8178, -7388, -6594, -5795, -4993, -4188, -3380, -2570, -1758, -946}}, + {97U, 246U, {0, 835, 1670, 2504, 3336, 4166, 4994, 5818, 6638, 7454, 8265, 9071, 9871, 10664, 11451, 12230, 13001, 13764, 14518, 15262, 15996, 16720, 17433, 18135, 18825, 19503, 20168, 20820, 21458, 22082, 22693, 23288, 23868, 24433, 24981, 25514, 26030, 26529, 27011, 27475, 27921, 28350, 28759, 29150, 29523, 29875, 30209, 30523, 30817, 31091, 31345, 31578, 31791, 31983, 32154, 32305, 32434, 32543, 32630, 32696, 32741, 32764, 32766, 32747, 32707, 32645, 32562, 32458, 32333, 32187, 32019, 31832, 31623, 31394, 31144, 30874, 30584, 30274, 29945, 29596, 29228, 28840, 28434, 28010, 27567, 27107, 26628, 26133, 25620, 25091, 24546, 23984, 23407, 22815, 22208, 21586, 20950, 20301, 19639, 18964, 18276, 17577, 16866, 16144, 15412, 14670, 13918, 13157, 12387, 11610, 10825, 10033, 9234, 8430, 7620, 6804, 5985, 5162, 4335, 3505, 2674, 1840, 1005, 170, -665, -1500, -2334, -3166, -3997, -4825, -5650, -6471, -7288, -8100, -8907, -9708, -10503, -11291, -12071, -12844, -13608, -14364, -15110, -15847, -16573, -17288, -17992, -18685, -19365, -20033, -20687, -21328, -21956, -22569, -23167, -23750, -24318, -24870, -25406, -25925, -26428, -26913, -27381, -27831, -28263, -28677, -29071, -29447, -29804, -30142, -30460, -30758, -31036, -31294, -31531, -31748, -31945, -32120, -32275, -32409, -32521, -32613, -32683, -32732, -32760, -32767, -32752, -32716, -32658, -32580, -32480, -32359, -32217, -32054, -31870, -31666, -31441, -31195, -30930, -30644, -30338, -30012, -29667, -29303, -28920, -28517, -28097, -27658, -27201, -26726, -26234, -25725, -25199, -24657, -24099, -23525, -22935, -22331, -21713, -21080, -20433, -19774, -19101, -18416, -17719, -17010, -16291, -15561, -14820, -14070, -13311, -12544, -11768, -10984, -10193, -9396, -8593, -7784, -6970, -6151, -5328, -4502, -3673, -2842, -2009, -1174}}, + {100U, 240U, {0, 858, 1715, 2571, 3425, 4277, 5126, 5971, 6813, 7650, 8481, 9307, 10126, 10938, 11743, 12540, 13328, 14107, 14876, 15636, 16384, 17121, 17847, 18560, 19261, 19948, 20622, 21281, 21926, 22556, 23170, 23769, 24351, 24917, 25466, 25997, 26510, 27005, 27482, 27939, 28378, 28797, 29197, 29576, 29935, 30274, 30592, 30888, 31164, 31419, 31651, 31863, 32052, 32219, 32365, 32488, 32588, 32667, 32723, 32757, 32768, 32757, 32723, 32667, 32588, 32488, 32365, 32219, 32052, 31863, 31651, 31419, 31164, 30888, 30592, 30274, 29935, 29576, 29197, 28797, 28378, 27939, 27482, 27005, 26510, 25997, 25466, 24917, 24351, 23769, 23170, 22556, 21926, 21281, 20622, 19948, 19261, 18560, 17847, 17121, 16384, 15636, 14876, 14107, 13328, 12540, 11743, 10938, 10126, 9307, 8481, 7650, 6813, 5971, 5126, 4277, 3425, 2571, 1715, 858, 0, -857, -1714, -2570, -3424, -4276, -5125, -5970, -6812, -7649, -8480, -9306, -10125, -10937, -11742, -12539, -13327, -14106, -14875, -15635, -16383, -17120, -17846, -18559, -19260, -19947, -20621, -21280, -21925, -22555, -23169, -23768, -24350, -24916, -25465, -25996, -26509, -27004, -27481, -27938, -28377, -28796, -29196, -29575, -29934, -30273, -30591, -30887, -31163, -31418, -31650, -31862, -32051, -32218, -32364, -32487, -32587, -32666, -32722, -32756, -32767, -32756, -32722, -32666, -32587, -32487, -32364, -32218, -32051, -31862, -31650, -31418, -31163, -30887, -30591, -30273, -29934, -29575, -29196, -28796, -28377, -27938, -27481, -27004, -26509, -25996, -25465, -24916, -24350, -23768, -23169, -22555, -21925, -21280, -20621, -19947, -19260, -18559, -17846, -17120, -16383, -15635, -14875, -14106, -13327, -12539, -11742, -10937, -10125, -9306, -8480, -7649, -6812, -5970, -5125, -4276, -3424, -2570, -1714, -857}}, + {103U, 232U, {0, 888, 1775, 2661, 3545, 4426, 5304, 6178, 7048, 7912, 8771, 9623, 10468, 11305, 12134, 12955, 13765, 14566, 15356, 16134, 16901, 17656, 18397, 19125, 19839, 20538, 21222, 21891, 22544, 23180, 23799, 24400, 24984, 25549, 26096, 26623, 27131, 27619, 28086, 28533, 28959, 29364, 29747, 30109, 30448, 30765, 31059, 31331, 31579, 31805, 32007, 32185, 32340, 32471, 32578, 32662, 32721, 32757, 32768, 32755, 32718, 32658, 32573, 32464, 32332, 32175, 31996, 31792, 31566, 31316, 31043, 30747, 30429, 30088, 29726, 29341, 28935, 28508, 28060, 27591, 27102, 26593, 26064, 25517, 24950, 24366, 23763, 23143, 22506, 21853, 21183, 20498, 19798, 19083, 18354, 17612, 16857, 16090, 15310, 14520, 13719, 12907, 12087, 11257, 10419, 9574, 8721, 7862, 6997, 6127, 5253, 4375, 3493, 2609, 1724, 836, -50, -938, -1825, -2711, -3595, -4476, -5354, -6228, -7097, -7961, -8819, -9671, -10516, -11353, -12181, -13001, -13811, -14611, -15400, -16178, -16944, -17698, -18439, -19166, -19879, -20577, -21261, -21928, -22580, -23215, -23833, -24433, -25016, -25580, -26126, -26652, -27159, -27645, -28112, -28557, -28982, -29386, -29768, -30128, -30466, -30782, -31075, -31345, -31592, -31816, -32017, -32194, -32347, -32477, -32583, -32665, -32723, -32757, -32767, -32753, -32715, -32652, -32566, -32456, -32322, -32165, -31983, -31779, -31551, -31299, -31025, -30728, -30409, -30067, -29703, -29317, -28910, -28481, -28032, -27562, -27072, -26562, -26032, -25483, -24916, -24330, -23727, -23106, -22468, -21813, -21143, -20457, -19756, -19040, -18311, -17568, -16812, -16044, -15264, -14473, -13671, -12859, -12038, -11208, -10369, -9523, -8670, -7811, -6946, -6076, -5201, -4323, -3441, -2557, -1671, -784}}, + {107U, 224U, {0, 920, 1838, 2756, 3671, 4583, 5492, 6396, 7295, 8189, 9076, 9956, 10828, 11692, 12546, 13391, 14225, 15047, 15858, 16657, 17442, 18213, 18971, 19713, 20440, 21150, 21844, 22521, 23180, 23821, 24443, 25046, 25629, 26192, 26734, 27255, 27755, 28233, 28688, 29121, 29531, 29918, 30282, 30621, 30936, 31227, 31494, 31735, 31952, 32143, 32309, 32450, 32565, 32654, 32718, 32756, 32768, 32754, 32715, 32650, 32559, 32442, 32300, 32132, 31939, 31721, 31478, 31211, 30918, 30601, 30261, 29896, 29508, 29096, 28662, 28205, 27726, 27225, 26702, 26159, 25595, 25010, 24406, 23783, 23141, 22481, 21803, 21108, 20397, 19669, 18926, 18168, 17395, 16609, 15810, 14999, 14175, 13340, 12495, 11640, 10776, 9904, 9023, 8136, 7242, 6342, 5438, 4529, 3616, 2701, 1783, 865, -54, -973, -1892, -2809, -3724, -4636, -5545, -6449, -7348, -8241, -9128, -10007, -10879, -11742, -12596, -13440, -14273, -15095, -15905, -16703, -17487, -18258, -19014, -19756, -20482, -21191, -21884, -22560, -23218, -23858, -24479, -25080, -25662, -26224, -26765, -27285, -27783, -28260, -28714, -29145, -29554, -29940, -30301, -30639, -30953, -31243, -31508, -31748, -31963, -32153, -32317, -32456, -32570, -32658, -32720, -32756, -32767, -32752, -32711, -32644, -32551, -32433, -32290, -32121, -31926, -31707, -31462, -31193, -30899, -30581, -30238, -29872, -29483, -29070, -28634, -28176, -27695, -27193, -26669, -26125, -25559, -24974, -24369, -23744, -23101, -22440, -21761, -21065, -20353, -19624, -18880, -18121, -17348, -16561, -15761, -14949, -14125, -13289, -12444, -11588, -10723, -9850, -8969, -8082, -7187, -6287, -5382, -4473, -3561, -2645, -1728, -809}}, + {110U, 216U, {0, 951, 1902, 2851, 3797, 4740, 5679, 6614, 7543, 8465, 9381, 10288, 11187, 12076, 12955, 13824, 14680, 15525, 16356, 17173, 17976, 18764, 19536, 20291, 21030, 21750, 22452, 23136, 23800, 24444, 25067, 25669, 26249, 26808, 27343, 27856, 28345, 28811, 29252, 29668, 30059, 30425, 30766, 31080, 31368, 31630, 31865, 32074, 32255, 32409, 32536, 32635, 32707, 32751, 32768, 32757, 32718, 32652, 32558, 32437, 32289, 32113, 31910, 31681, 31424, 31142, 30833, 30497, 30137, 29750, 29339, 28903, 28443, 27959, 27451, 26920, 26366, 25790, 25192, 24573, 23934, 23274, 22595, 21896, 21179, 20444, 19692, 18924, 18139, 17339, 16525, 15697, 14855, 14001, 13135, 12258, 11371, 10474, 9568, 8654, 7733, 6805, 5872, 4934, 3991, 3045, 2097, 1147, 196, -755, -1705, -2655, -3602, -4546, -5486, -6421, -7351, -8275, -9192, -10101, -11002, -11893, -12775, -13645, -14504, -15351, -16185, -17005, -17811, -18602, -19377, -20136, -20878, -21603, -22309, -22996, -23664, -24312, -24939, -25546, -26131, -26694, -27234, -27752, -28246, -28716, -29162, -29583, -29980, -30351, -30697, -31017, -31310, -31578, -31818, -32032, -32219, -32379, -32511, -32616, -32693, -32743, -32766, -32760, -32727, -32667, -32579, -32463, -32321, -32151, -31953, -31729, -31478, -31201, -30897, -30567, -30212, -29831, -29425, -28994, -28539, -28059, -27556, -27030, -26481, -25909, -25316, -24701, -24066, -23410, -22735, -22040, -21327, -20596, -19847, -19082, -18301, -17504, -16693, -15867, -15028, -14176, -13313, -12438, -11553, -10658, -9754, -8842, -7922, -6996, -6063, -5126, -4184, -3239, -2291, -1341}}, + {114U, 209U, {0, 985, 1968, 2950, 3930, 4906, 5877, 6843, 7803, 8756, 9701, 10637, 11563, 12480, 13384, 14277, 15157, 16023, 16875, 17711, 18532, 19335, 20122, 20890, 21639, 22369, 23078, 23767, 24434, 25079, 25701, 26301, 26876, 27427, 27954, 28455, 28930, 29380, 29802, 30198, 30567, 30908, 31221, 31506, 31762, 31990, 32189, 32359, 32499, 32610, 32692, 32744, 32767, 32760, 32723, 32657, 32562, 32437, 32282, 32099, 31886, 31645, 31375, 31077, 30751, 30397, 30015, 29607, 29172, 28710, 28222, 27709, 27171, 26608, 26022, 25411, 24778, 24123, 23445, 22747, 22028, 21289, 20531, 19754, 18960, 18148, 17320, 16476, 15617, 14745, 13859, 12960, 12050, 11129, 10198, 9257, 8309, 7352, 6389, 5421, 4447, 3470, 2489, 1506, 522, -462, -1447, -2430, -3411, -4388, -5362, -6331, -7294, -8251, -9200, -10141, -11073, -11995, -12906, -13805, -14692, -15565, -16425, -17269, -18098, -18911, -19706, -20484, -21244, -21984, -22704, -23404, -24082, -24739, -25374, -25985, -26573, -27137, -27677, -28192, -28681, -29144, -29581, -29991, -30374, -30730, -31058, -31358, -31629, -31872, -32086, -32271, -32427, -32554, -32652, -32719, -32758, -32766, -32745, -32695, -32615, -32506, -32367, -32199, -32002, -31776, -31521, -31238, -30926, -30587, -30220, -29826, -29404, -28957, -28483, -27983, -27458, -26908, -26334, -25737, -25115, -24472, -23806, -23118, -22410, -21682, -20934, -20167, -19381, -18579, -17759, -16924, -16073, -15208, -14329, -13437, -12532, -11617, -10691, -9755, -8811, -7859, -6899, -5933, -4962, -3987, -3008, -2026, -1042}}, + {118U, 202U, {0, 1019, 2037, 3053, 4066, 5075, 6079, 7078, 8069, 9053, 10028, 10993, 11948, 12891, 13821, 14739, 15642, 16529, 17401, 18256, 19094, 19912, 20712, 21492, 22251, 22988, 23703, 24395, 25063, 25708, 26327, 26921, 27489, 28030, 28545, 29031, 29490, 29920, 30321, 30692, 31035, 31347, 31628, 31879, 32100, 32289, 32447, 32573, 32669, 32732, 32764, 32764, 32733, 32669, 32575, 32448, 32291, 32102, 31882, 31631, 31350, 31038, 30696, 30325, 29924, 29494, 29036, 28550, 28036, 27495, 26927, 26333, 25714, 25070, 24402, 23710, 22995, 22258, 21499, 20720, 19921, 19102, 18265, 17410, 16538, 15651, 14748, 13831, 12900, 11957, 11003, 10038, 9063, 8079, 7088, 6090, 5085, 4076, 3063, 2047, 1029, 10, -1008, -2026, -3042, -4055, -5064, -6068, -7067, -8058, -9042, -10017, -10982, -11937, -12880, -13811, -14728, -15632, -16520, -17392, -18247, -19084, -19903, -20703, -21483, -22242, -22979, -23695, -24387, -25056, -25700, -26320, -26914, -27482, -28024, -28539, -29025, -29484, -29914, -30316, -30688, -31030, -31343, -31625, -31876, -32097, -32286, -32444, -32571, -32667, -32731, -32763, -32763, -32732, -32669, -32575, -32449, -32291, -32103, -31883, -31633, -31352, -31040, -30699, -30328, -29927, -29498, -29040, -28554, -28040, -27499, -26932, -26338, -25720, -25076, -24408, -23716, -23001, -22265, -21506, -20727, -19928, -19109, -18272, -17418, -16546, -15659, -14756, -13839, -12909, -11966, -11012, -10047, -9072, -8088, -7097, -6099, -5095, -4085, -3072, -2057, -1039}}, + {123U, 195U, {0, 1055, 2109, 3161, 4209, 5253, 6292, 7324, 8348, 9364, 10370, 11366, 12349, 13320, 14277, 15219, 16146, 17055, 17947, 18821, 19675, 20508, 21320, 22110, 22877, 23621, 24340, 25034, 25701, 26342, 26956, 27542, 28099, 28628, 29126, 29594, 30032, 30438, 30813, 31156, 31467, 31745, 31990, 32202, 32381, 32525, 32637, 32714, 32758, 32767, 32743, 32684, 32592, 32466, 32306, 32113, 31886, 31627, 31335, 31010, 30653, 30264, 29844, 29393, 28911, 28399, 27858, 27288, 26690, 26064, 25411, 24732, 24027, 23298, 22544, 21766, 20966, 20145, 19302, 18440, 17558, 16658, 15741, 14808, 13859, 12896, 11919, 10930, 9930, 8919, 7900, 6872, 5836, 4795, 3749, 2699, 1646, 592, -462, -1517, -2570, -3620, -4667, -5709, -6745, -7774, -8794, -9806, -10808, -11798, -12776, -13741, -14692, -15627, -16546, -17448, -18332, -19197, -20042, -20866, -21669, -22449, -23206, -23939, -24647, -25329, -25985, -26614, -27216, -27789, -28334, -28849, -29334, -29789, -30213, -30606, -30967, -31296, -31592, -31856, -32086, -32283, -32447, -32577, -32674, -32736, -32765, -32760, -32720, -32647, -32540, -32399, -32225, -32017, -31776, -31502, -31195, -30856, -30485, -30082, -29648, -29184, -28689, -28164, -27611, -27028, -26418, -25780, -25115, -24425, -23709, -22968, -22204, -21417, -20607, -19776, -18925, -18054, -17164, -16257, -15332, -14392, -13437, -12467, -11485, -10491, -9486, -8472, -7448, -6417, -5379, -4336, -3288, -2236, -1183}}, + {127U, 189U, {0, 1092, 2183, 3271, 4355, 5435, 6509, 7575, 8633, 9682, 10720, 11745, 12758, 13757, 14740, 15707, 16657, 17588, 18499, 19390, 20260, 21106, 21930, 22729, 23503, 24251, 24972, 25665, 26329, 26965, 27570, 28145, 28688, 29200, 29679, 30126, 30538, 30917, 31262, 31572, 31847, 32086, 32290, 32458, 32590, 32685, 32745, 32768, 32754, 32705, 32619, 32496, 32338, 32144, 31914, 31648, 31348, 31012, 30642, 30238, 29801, 29330, 28827, 28292, 27726, 27128, 26501, 25844, 25158, 24445, 23704, 22937, 22144, 21327, 20487, 19623, 18738, 17832, 16906, 15961, 14999, 14019, 13025, 12016, 10993, 9958, 8913, 7857, 6793, 5721, 4643, 3559, 2472, 1382, 290, -801, -1892, -2981, -4067, -5148, -6223, -7292, -8352, -9403, -10444, -11473, -12490, -13492, -14480, -15451, -16405, -17341, -18258, -19155, -20030, -20883, -21713, -22518, -23299, -24054, -24782, -25482, -26155, -26798, -27411, -27994, -28546, -29066, -29554, -30009, -30431, -30819, -31173, -31492, -31776, -32025, -32238, -32416, -32557, -32662, -32731, -32764, -32760, -32720, -32644, -32531, -32382, -32198, -31977, -31721, -31430, -31104, -30743, -30348, -29919, -29458, -28963, -28436, -27878, -27289, -26669, -26020, -25342, -24636, -23902, -23142, -22356, -21546, -20711, -19854, -18974, -18073, -17152, -16213, -15255, -14280, -13289, -12284, -11265, -10233, -9190, -8137, -7075, -6005, -4928, -3846, -2760, -1670, -579}}, + {131U, 182U, {0, 1130, 2260, 3386, 4508, 5625, 6736, 7838, 8931, 10013, 11084, 12141, 13184, 14211, 15221, 16213, 17186, 18138, 19068, 19976, 20861, 21720, 22554, 23360, 24139, 24889, 25610, 26300, 26958, 27585, 28179, 28739, 29265, 29756, 30212, 30631, 31015, 31361, 31670, 31941, 32175, 32370, 32526, 32644, 32723, 32763, 32764, 32725, 32648, 32533, 32378, 32185, 31953, 31684, 31376, 31032, 30650, 30232, 29778, 29289, 28764, 28206, 27613, 26988, 26331, 25643, 24924, 24175, 23397, 22592, 21760, 20902, 20019, 19112, 18182, 17231, 16259, 15268, 14259, 13232, 12190, 11134, 10064, 8982, 7890, 6788, 5678, 4561, 3439, 2313, 1184, 53, -1076, -2205, -3332, -4455, -5572, -6683, -7785, -8879, -9962, -11033, -12090, -13134, -14162, -15173, -16166, -17139, -18093, -19024, -19933, -20819, -21679, -22514, -23322, -24102, -24853, -25575, -26267, -26927, -27555, -28150, -28712, -29240, -29733, -30190, -30611, -30996, -31345, -31655, -31928, -32164, -32360, -32519, -32638, -32719, -32761, -32763, -32727, -32652, -32538, -32385, -32194, -31964, -31696, -31391, -31048, -30668, -30252, -29799, -29311, -28789, -28232, -27641, -27018, -26362, -25675, -24957, -24210, -23434, -22630, -21799, -20942, -20060, -19154, -18225, -17275, -16304, -15314, -14305, -13280, -12239, -11183, -10113, -9032, -7940, -6839, -5729, -4613, -3491, -2365, -1236}}, + {136U, 176U, {0, 1171, 2340, 3506, 4668, 5824, 6972, 8112, 9241, 10358, 11462, 12552, 13625, 14681, 15718, 16736, 17731, 18705, 19654, 20578, 21476, 22347, 23189, 24001, 24783, 25533, 26250, 26934, 27584, 28198, 28777, 29318, 29822, 30288, 30716, 31104, 31453, 31761, 32029, 32255, 32441, 32585, 32688, 32749, 32768, 32745, 32681, 32574, 32426, 32237, 32007, 31735, 31423, 31071, 30680, 30249, 29780, 29272, 28727, 28146, 27528, 26876, 26189, 25468, 24715, 23931, 23116, 22271, 21398, 20498, 19572, 18620, 17645, 16647, 15628, 14589, 13531, 12457, 11366, 10260, 9142, 8012, 6872, 5722, 4566, 3404, 2237, 1068, -102, -1273, -2442, -3608, -4769, -5924, -7072, -8210, -9339, -10455, -11558, -12646, -13718, -14772, -15808, -16823, -17817, -18788, -19735, -20657, -21553, -22421, -23260, -24070, -24849, -25596, -26311, -26992, -27638, -28250, -28825, -29363, -29864, -30327, -30751, -31135, -31480, -31785, -32049, -32272, -32454, -32595, -32694, -32751, -32767, -32740, -32672, -32562, -32410, -32218, -31983, -31709, -31393, -31038, -30643, -30208, -29735, -29225, -28677, -28092, -27471, -26816, -26126, -25402, -24647, -23859, -23042, -22195, -21319, -20417, -19488, -18534, -17557, -16557, -15536, -14496, -13437, -12360, -11268, -10162, -9042, -7911, -6770, -5620, -4463, -3300, -2134, -964}}, + {141U, 170U, {0, 1212, 2422, 3629, 4831, 6026, 7213, 8391, 9556, 10709, 11847, 12969, 14073, 15158, 16222, 17264, 18282, 19275, 20242, 21181, 22091, 22971, 23820, 24636, 25418, 26165, 26877, 27552, 28189, 28788, 29347, 29866, 30344, 30781, 31175, 31527, 31836, 32101, 32322, 32500, 32632, 32720, 32763, 32762, 32715, 32624, 32488, 32308, 32083, 31815, 31503, 31147, 30750, 30310, 29829, 29307, 28745, 28143, 27503, 26825, 26111, 25361, 24576, 23758, 22907, 22025, 21112, 20171, 19202, 18207, 17187, 16143, 15078, 13992, 12886, 11763, 10624, 9470, 8304, 7126, 5938, 4742, 3539, 2332, 1122, -89, -1301, -2511, -3718, -4919, -6114, -7300, -8477, -9641, -10793, -11930, -13051, -14153, -15237, -16299, -17339, -18356, -19347, -20312, -21249, -22157, -23034, -23881, -24694, -25474, -26219, -26927, -27600, -28234, -28830, -29386, -29902, -30377, -30811, -31202, -31551, -31856, -32118, -32336, -32510, -32639, -32724, -32764, -32759, -32709, -32614, -32475, -32291, -32064, -31792, -31477, -31118, -30718, -30275, -29790, -29265, -28700, -28096, -27453, -26773, -26056, -25303, -24516, -23695, -22841, -21957, -21042, -20099, -19128, -18131, -17109, -16064, -14997, -13909, -12802, -11678, -10538, -9383, -8215, -7037, -5848, -4652, -3449, -2241, -1031}}, + {146U, 164U, {0, 1254, 2506, 3754, 4997, 6233, 7459, 8675, 9878, 11066, 12238, 13392, 14527, 15640, 16730, 17796, 18836, 19848, 20832, 21784, 22705, 23592, 24445, 25262, 26042, 26784, 27487, 28150, 28771, 29350, 29886, 30378, 30826, 31229, 31586, 31896, 32160, 32377, 32546, 32668, 32742, 32768, 32746, 32676, 32558, 32392, 32179, 31919, 31612, 31259, 30860, 30415, 29927, 29394, 28818, 28200, 27541, 26842, 26103, 25326, 24511, 23661, 22777, 21858, 20908, 19927, 18917, 17880, 16816, 15727, 14616, 13483, 12330, 11159, 9972, 8771, 7556, 6330, 5096, 3853, 2605, 1353, 100, -1153, -2406, -3654, -4898, -6134, -7361, -8578, -9782, -10971, -12145, -13300, -14436, -15552, -16644, -17712, -18754, -19768, -20754, -21709, -22632, -23522, -24378, -25198, -25981, -26726, -27432, -28098, -28722, -29305, -29844, -30340, -30791, -31198, -31558, -31872, -32140, -32361, -32534, -32659, -32737, -32767, -32748, -32682, -32568, -32406, -32197, -31940, -31637, -31287, -30892, -30451, -29966, -29437, -28865, -28250, -27594, -26898, -26162, -25388, -24576, -23729, -22847, -21931, -20984, -20005, -18998, -17962, -16900, -15814, -14704, -13573, -12421, -11252, -10066, -8865, -7652, -6427, -5193, -3951, -2703, -1452}}, + {151U, 159U, {0, 1298, 2595, 3887, 5173, 6452, 7720, 8975, 10217, 11443, 12651, 13839, 15005, 16147, 17264, 18354, 19416, 20446, 21445, 22410, 23340, 24233, 25088, 25903, 26678, 27411, 28101, 28747, 29348, 29902, 30410, 30870, 31281, 31643, 31955, 32218, 32429, 32590, 32700, 32758, 32764, 32720, 32624, 32476, 32278, 32029, 31729, 31380, 30981, 30534, 30039, 29496, 28908, 28274, 27595, 26873, 26109, 25304, 24459, 23576, 22655, 21699, 20709, 19687, 18633, 17551, 16440, 15304, 14144, 12962, 11759, 10538, 9300, 8048, 6783, 5507, 4223, 2932, 1636, 338, -960, -2257, -3550, -4838, -6119, -7390, -8649, -9895, -11125, -12337, -13530, -14702, -15851, -16975, -18072, -19141, -20180, -21187, -22161, -23100, -24003, -24868, -25694, -26480, -27224, -27925, -28582, -29195, -29761, -30281, -30754, -31178, -31553, -31878, -32153, -32378, -32552, -32675, -32747, -32767, -32735, -32653, -32518, -32333, -32097, -31811, -31475, -31089, -30654, -30171, -29641, -29064, -28442, -27775, -27064, -26311, -25516, -24682, -23808, -22897, -21951, -20969, -19955, -18909, -17834, -16731, -15601, -14447, -13270, -12073, -10856, -9623, -8374, -7112, -5839, -4557, -3267, -1973, -675}}, + {156U, 153U, {0, 1344, 2686, 4023, 5353, 6674, 7984, 9281, 10562, 11825, 13069, 14290, 15488, 16659, 17802, 18915, 19997, 21045, 22057, 23032, 23969, 24865, 25719, 26531, 27297, 28017, 28691, 29316, 29892, 30417, 30891, 31314, 31683, 31999, 32262, 32470, 32624, 32722, 32766, 32754, 32687, 32566, 32389, 32158, 31873, 31534, 31142, 30698, 30202, 29656, 29059, 28413, 27720, 26980, 26195, 25365, 24493, 23580, 22627, 21636, 20608, 19546, 18451, 17325, 16170, 14987, 13779, 12548, 11296, 10025, 8738, 7435, 6120, 4794, 3461, 2122, 779, -564, -1908, -3248, -4582, -5909, -7226, -8531, -9821, -11095, -12350, -13584, -14796, -15982, -17142, -18273, -19373, -20441, -21474, -22471, -23430, -24350, -25229, -26065, -26857, -27605, -28305, -28959, -29563, -30118, -30622, -31074, -31475, -31822, -32115, -32355, -32540, -32671, -32746, -32767, -32732, -32642, -32497, -32298, -32044, -31736, -31375, -30961, -30495, -29978, -29410, -28792, -28127, -27414, -26654, -25850, -25003, -24113, -23183, -22214, -21207, -20165, -19088, -17980, -16841, -15674, -14481, -13263, -12023, -10763, -9485, -8190, -6882, -5563, -4234, -2897, -1556}}, + {159U, 150U, {0, 1370, 2739, 4102, 5458, 6804, 8139, 9459, 10763, 12048, 13312, 14553, 15768, 16956, 18114, 19240, 20332, 21389, 22409, 23389, 24328, 25225, 26078, 26885, 27645, 28356, 29018, 29630, 30189, 30695, 31148, 31547, 31890, 32177, 32408, 32582, 32699, 32759, 32762, 32707, 32596, 32427, 32201, 31919, 31581, 31188, 30740, 30239, 29684, 29078, 28421, 27714, 26958, 26156, 25307, 24414, 23479, 22502, 21487, 20433, 19344, 18221, 17066, 15881, 14668, 13430, 12168, 10885, 9583, 8264, 6930, 5585, 4229, 2867, 1499, 129, -1241, -2609, -3973, -5330, -6678, -8013, -9335, -10641, -11928, -13194, -14436, -15654, -16844, -18005, -19134, -20230, -21291, -22314, -23298, -24241, -25142, -25999, -26810, -27575, -28291, -28957, -29573, -30138, -30649, -31107, -31511, -31859, -32151, -32388, -32567, -32690, -32755, -32763, -32714, -32608, -32444, -32224, -31947, -31614, -31226, -30784, -30287, -29738, -29136, -28484, -27781, -27030, -26232, -25388, -24499, -23568, -22595, -21583, -20532, -19446, -18326, -17174, -15992, -14782, -13546, -12286, -11005, -9705, -8387, -7055, -5711, -4356, -2994, -1627}}, + {162U, 148U, {0, 1391, 2780, 4163, 5539, 6905, 8259, 9597, 10919, 12220, 13500, 14755, 15984, 17184, 18353, 19489, 20590, 21653, 22678, 23661, 24602, 25499, 26350, 27153, 27907, 28611, 29263, 29863, 30408, 30899, 31335, 31713, 32035, 32298, 32504, 32651, 32739, 32768, 32738, 32649, 32501, 32294, 32030, 31707, 31327, 30891, 30399, 29853, 29252, 28599, 27894, 27139, 26335, 25484, 24586, 23645, 22660, 21635, 20571, 19470, 18333, 17164, 15963, 14734, 13478, 12198, 10896, 9574, 8235, 6882, 5515, 4139, 2756, 1367, -23, -1414, -2803, -4186, -5562, -6928, -8281, -9619, -10940, -12242, -13521, -14776, -16004, -17204, -18372, -19507, -20607, -21670, -22694, -23677, -24617, -25513, -26363, -27165, -27919, -28622, -29273, -29872, -30416, -30906, -31341, -31718, -32039, -32302, -32506, -32652, -32739, -32767, -32736, -32646, -32497, -32289, -32024, -31700, -31319, -30882, -30390, -29842, -29241, -28586, -27881, -27125, -26320, -25468, -24570, -23627, -22642, -21616, -20551, -19449, -18312, -17142, -15941, -14712, -13455, -12175, -10872, -9550, -8211, -6857, -5491, -4114, -2731, -1342}}, + {165U, 145U, {0, 1419, 2836, 4247, 5651, 7043, 8423, 9787, 11132, 12457, 13758, 15033, 16280, 17496, 18680, 19829, 20940, 22012, 23043, 24030, 24973, 25868, 26715, 27512, 28257, 28949, 29587, 30169, 30695, 31163, 31572, 31923, 32213, 32443, 32612, 32720, 32766, 32751, 32674, 32536, 32337, 32078, 31758, 31378, 30940, 30443, 29890, 29280, 28615, 27897, 27126, 26304, 25433, 24514, 23549, 22540, 21489, 20397, 19267, 18101, 16901, 15669, 14408, 13120, 11807, 10472, 9117, 7745, 6359, 4961, 3553, 2139, 721, -698, -2116, -3531, -4939, -6337, -7724, -9096, -10451, -11786, -13099, -14388, -15650, -16882, -18082, -19249, -20380, -21472, -22524, -23534, -24499, -25419, -26290, -27113, -27884, -28604, -29269, -29880, -30434, -30932, -31371, -31751, -32072, -32333, -32533, -32672, -32749, -32765, -32720, -32613, -32445, -32216, -31927, -31577, -31169, -30701, -30177, -29595, -28958, -28267, -27523, -26726, -25880, -24985, -24044, -23057, -22027, -20955, -19845, -18697, -17513, -16297, -15051, -13776, -12475, -11151, -9806, -8443, -7063, -5671, -4268, -2856, -1440}}, + {167U, 143U, {0, 1440, 2877, 4309, 5732, 7144, 8542, 9924, 11287, 12628, 13944, 15234, 16494, 17722, 18916, 20074, 21192, 22270, 23305, 24294, 25237, 26131, 26975, 27766, 28504, 29187, 29813, 30382, 30892, 31342, 31732, 32061, 32327, 32531, 32673, 32751, 32766, 32717, 32606, 32431, 32194, 31895, 31534, 31112, 30630, 30088, 29489, 28833, 28121, 27354, 26535, 25665, 24745, 23777, 22763, 21705, 20606, 19466, 18289, 17077, 15831, 14555, 13251, 11921, 10569, 9196, 7805, 6399, 4980, 3552, 2117, 679, -761, -2199, -3634, -5061, -6479, -7884, -9274, -10646, -11998, -13326, -14629, -15903, -17147, -18357, -19532, -20669, -21767, -22822, -23833, -24798, -25715, -26583, -27399, -28162, -28871, -29524, -30120, -30658, -31137, -31555, -31913, -32208, -32442, -32613, -32721, -32766, -32747, -32665, -32520, -32313, -32042, -31710, -31317, -30863, -30350, -29777, -29148, -28462, -27721, -26926, -26080, -25183, -24238, -23245, -22208, -21128, -20007, -18847, -17651, -16421, -15159, -13868, -12550, -11208, -9844, -8461, -7062, -5649, -4225, -2793, -1356}}, + {171U, 140U, {0, 1469, 2935, 4395, 5847, 7286, 8711, 10119, 11506, 12870, 14208, 15517, 16795, 18040, 19248, 20418, 21546, 22631, 23671, 24663, 25605, 26496, 27334, 28117, 28843, 29511, 30120, 30668, 31155, 31579, 31939, 32235, 32467, 32633, 32733, 32768, 32737, 32640, 32477, 32249, 31956, 31599, 31179, 30695, 30150, 29544, 28879, 28156, 27376, 26542, 25653, 24714, 23724, 22687, 21604, 20478, 19310, 18104, 16862, 15585, 14277, 12940, 11578, 10192, 8786, 7361, 5923, 4472, 3012, 1546, 77, -1391, -2857, -4318, -5770, -7210, -8636, -10044, -11432, -12797, -14137, -15448, -16728, -17974, -19185, -20356, -21487, -22574, -23616, -24611, -25556, -26450, -27290, -28076, -28805, -29476, -30088, -30640, -31130, -31557, -31921, -32220, -32455, -32625, -32729, -32767, -32739, -32645, -32486, -32262, -31972, -31618, -31201, -30721, -30179, -29577, -28915, -28195, -27418, -26586, -25700, -24763, -23776, -22742, -21661, -20537, -19372, -18168, -16927, -15652, -14346, -13010, -11649, -10264, -8859, -7436, -5997, -4547, -3088, -1622}}, + {173U, 138U, {0, 1490, 2978, 4459, 5931, 7391, 8835, 10261, 11666, 13047, 14401, 15724, 17016, 18272, 19490, 20668, 21803, 22893, 23936, 24929, 25871, 26759, 27591, 28367, 29084, 29740, 30335, 30867, 31336, 31739, 32077, 32348, 32552, 32689, 32759, 32760, 32694, 32560, 32359, 32090, 31756, 31355, 30890, 30360, 29768, 29114, 28400, 27627, 26797, 25912, 24973, 23982, 22941, 21853, 20720, 19544, 18327, 17073, 15783, 14461, 13108, 11729, 10325, 8900, 7456, 5997, 4525, 3044, 1557, 67, -1423, -2910, -4392, -5864, -7324, -8770, -10197, -11603, -12984, -14339, -15665, -16958, -18215, -19435, -20615, -21752, -22845, -23889, -24885, -25829, -26719, -27554, -28332, -29052, -29711, -30309, -30844, -31315, -31721, -32062, -32336, -32544, -32684, -32756, -32761, -32697, -32566, -32368, -32103, -31771, -31373, -30911, -30384, -29795, -29144, -28433, -27662, -26835, -25952, -25015, -24026, -22988, -21902, -20771, -19597, -18382, -17129, -15841, -14520, -13168, -11790, -10387, -8963, -7520, -6062, -4591, -3110, -1623}}, + {177U, 135U, {0, 1520, 3038, 4548, 6049, 7537, 9008, 10461, 11890, 13294, 14670, 16013, 17323, 18595, 19827, 21016, 22160, 23256, 24302, 25296, 26235, 27118, 27942, 28706, 29408, 30047, 30622, 31130, 31571, 31944, 32249, 32484, 32648, 32743, 32767, 32721, 32604, 32416, 32159, 31833, 31438, 30975, 30446, 29851, 29192, 28470, 27686, 26843, 25942, 24985, 23975, 22912, 21801, 20642, 19439, 18194, 16910, 15590, 14235, 12851, 11438, 10001, 8542, 7065, 5573, 4069, 2556, 1037, -483, -2003, -3518, -5026, -6523, -8006, -9472, -10917, -12339, -13734, -15100, -16433, -17730, -18990, -20209, -21384, -22513, -23593, -24623, -25599, -26521, -27385, -28191, -28935, -29618, -30236, -30789, -31277, -31696, -32048, -32330, -32543, -32685, -32757, -32759, -32690, -32551, -32341, -32062, -31714, -31297, -30813, -30263, -29647, -28968, -28226, -27423, -26562, -25643, -24669, -23641, -22563, -21436, -20263, -19047, -17789, -16493, -15161, -13797, -12403, -10982, -9538, -8073, -6591, -5095, -3587, -2072}}, + {179U, 133U, {0, 1543, 3082, 4615, 6137, 7645, 9137, 10608, 12056, 13477, 14869, 16227, 17549, 18833, 20074, 21271, 22421, 23521, 24569, 25563, 26500, 27378, 28195, 28950, 29641, 30265, 30823, 31313, 31733, 32082, 32361, 32567, 32702, 32764, 32753, 32669, 32513, 32286, 31986, 31615, 31175, 30665, 30087, 29443, 28733, 27959, 27124, 26228, 25274, 24265, 23201, 22086, 20921, 19711, 18457, 17161, 15828, 14460, 13059, 11630, 10175, 8697, 7200, 5687, 4161, 2627, 1086, -456, -1998, -3536, -5066, -6584, -8088, -9574, -11039, -12479, -13892, -15274, -16622, -17933, -19204, -20433, -21616, -22751, -23836, -24869, -25846, -26765, -27625, -28424, -29160, -29832, -30437, -30974, -31443, -31842, -32171, -32428, -32614, -32727, -32767, -32735, -32630, -32452, -32203, -31883, -31491, -31030, -30500, -29902, -29238, -28509, -27717, -26864, -25951, -24980, -23954, -22875, -21745, -20567, -19343, -18076, -16769, -15425, -14047, -12638, -11200, -9738, -8254, -6752, -5235, -3706, -2170}}, + {183U, 131U, {0, 1574, 3144, 4706, 6258, 7795, 9315, 10813, 12286, 13730, 15143, 16521, 17861, 19160, 20414, 21622, 22779, 23884, 24934, 25926, 26858, 27729, 28535, 29276, 29949, 30553, 31086, 31548, 31937, 32252, 32493, 32659, 32750, 32765, 32704, 32568, 32356, 32071, 31711, 31278, 30772, 30196, 29550, 28836, 28055, 27210, 26302, 25333, 24305, 23222, 22085, 20897, 19661, 18379, 17055, 15692, 14293, 12860, 11398, 9909, 8398, 6867, 5321, 3762, 2194, 622, -951, -2523, -4089, -5645, -7189, -8716, -10223, -11706, -13162, -14588, -15980, -17335, -18651, -19923, -21149, -22327, -23453, -24525, -25540, -26496, -27391, -28223, -28990, -29690, -30322, -30883, -31373, -31791, -32136, -32406, -32602, -32722, -32767, -32736, -32630, -32448, -32191, -31861, -31456, -30979, -30431, -29812, -29125, -28370, -27550, -26667, -25722, -24717, -23656, -22539, -21371, -20154, -18890, -17582, -16234, -14849, -13429, -11978, -10499, -8997, -7473, -5933, -4378, -2814, -1243}}, + {186U, 129U, {0, 1597, 3190, 4775, 6349, 7908, 9448, 10966, 12457, 13919, 15348, 16741, 18094, 19403, 20667, 21881, 23044, 24152, 25202, 26193, 27121, 27985, 28782, 29511, 30170, 30757, 31272, 31711, 32076, 32364, 32576, 32710, 32766, 32745, 32645, 32468, 32214, 31884, 31477, 30996, 30442, 29815, 29117, 28350, 27515, 26615, 25652, 24628, 23546, 22407, 21216, 19974, 18684, 17350, 15975, 14562, 13114, 11636, 10129, 8599, 7048, 5480, 3899, 2309, 714, -882, -2478, -4067, -5646, -7212, -8761, -10290, -11793, -13269, -14713, -16122, -17493, -18823, -20107, -21344, -22530, -23663, -24739, -25757, -26713, -27606, -28433, -29193, -29884, -30503, -31050, -31523, -31921, -32244, -32490, -32658, -32750, -32763, -32698, -32556, -32336, -32040, -31667, -31219, -30697, -30103, -29436, -28700, -27895, -27024, -26089, -25092, -24036, -22922, -21754, -20534, -19265, -17951, -16594, -15197, -13764, -12299, -10804, -9284, -7742, -6181, -4606, -3020, -1426}}, + {189U, 126U, {0, 1628, 3253, 4869, 6473, 8062, 9630, 11175, 12692, 14177, 15628, 17040, 18410, 19734, 21010, 22233, 23402, 24513, 25563, 26551, 27472, 28326, 29110, 29821, 30459, 31022, 31508, 31916, 32246, 32496, 32665, 32754, 32761, 32688, 32534, 32300, 31986, 31592, 31121, 30573, 29949, 29251, 28481, 27641, 26732, 25757, 24719, 23619, 22461, 21248, 19982, 18667, 17305, 15901, 14458, 12978, 11467, 9927, 8363, 6778, 5177, 3563, 1939, 311, -1316, -2942, -4560, -6167, -7759, -9331, -10881, -12403, -13895, -15353, -16772, -18150, -19484, -20769, -22003, -23182, -24304, -25367, -26366, -27300, -28167, -28964, -29690, -30342, -30919, -31420, -31844, -32188, -32453, -32638, -32742, -32765, -32707, -32569, -32350, -32051, -31673, -31216, -30682, -30073, -29389, -28633, -27806, -26910, -25947, -24921, -23833, -22686, -21483, -20227, -18921, -17568, -16172, -14735, -13263, -11757, -10223, -8663, -7082, -5483, -3871, -2249}}, + {192U, 124U, {0, 1653, 3302, 4943, 6571, 8182, 9773, 11338, 12875, 14379, 15846, 17273, 18656, 19991, 21276, 22506, 23679, 24792, 25841, 26825, 27740, 28585, 29357, 30054, 30674, 31217, 31680, 32062, 32362, 32581, 32716, 32767, 32736, 32621, 32422, 32142, 31779, 31336, 30812, 30210, 29531, 28777, 27950, 27052, 26084, 25050, 23953, 22794, 21577, 20305, 18982, 17610, 16193, 14736, 13240, 11711, 10152, 8567, 6960, 5336, 3698, 2051, 398, -1255, -2905, -4548, -6180, -7795, -9391, -10963, -12507, -14019, -15496, -16933, -18326, -19674, -20971, -22214, -23401, -24529, -25594, -26593, -27525, -28387, -29177, -29892, -30531, -31092, -31575, -31976, -32297, -32535, -32690, -32762, -32750, -32655, -32477, -32216, -31873, -31449, -30944, -30361, -29701, -28965, -28155, -27273, -26322, -25304, -24221, -23077, -21874, -20615, -19304, -17943, -16537, -15089, -13602, -12081, -10529, -8950, -7348, -5727, -4092, -2447}}, + {196U, 122U, {0, 1686, 3367, 5040, 6699, 8340, 9959, 11552, 13114, 14642, 16131, 17577, 18976, 20326, 21621, 22859, 24037, 25151, 26198, 27176, 28082, 28913, 29668, 30345, 30941, 31455, 31886, 32232, 32493, 32668, 32756, 32758, 32673, 32501, 32243, 31900, 31473, 30962, 30369, 29695, 28943, 28114, 27211, 26236, 25191, 24080, 22904, 21669, 20375, 19028, 17630, 16186, 14699, 13173, 11612, 10020, 8401, 6761, 5102, 3430, 1749, 63, -1621, -3303, -4976, -6636, -8278, -9898, -11492, -13055, -14584, -16074, -17522, -18924, -20275, -21572, -22813, -23992, -25109, -26159, -27139, -28048, -28882, -29640, -30320, -30919, -31436, -31870, -32220, -32484, -32662, -32754, -32758, -32677, -32508, -32254, -31914, -31489, -30981, -30391, -29721, -28972, -28146, -27246, -26273, -25231, -24122, -22949, -21715, -20424, -19079, -17683, -16240, -14754, -13230, -11670, -10079, -8462, -6822, -5164, -3492, -1812}}, + {199U, 120U, {0, 1711, 3417, 5113, 6796, 8460, 10101, 11715, 13297, 14842, 16347, 17807, 19219, 20578, 21881, 23125, 24305, 25420, 26464, 27437, 28335, 29155, 29897, 30556, 31132, 31624, 32029, 32346, 32576, 32716, 32768, 32730, 32603, 32386, 32082, 31690, 31212, 30648, 30001, 29272, 28463, 27577, 26615, 25581, 24477, 23307, 22072, 20778, 19427, 18023, 16569, 15071, 13531, 11955, 10346, 8709, 7048, 5367, 3673, 1968, 257, -1453, -3160, -4858, -6543, -8210, -9855, -11473, -13060, -14611, -16122, -17590, -19009, -20376, -21688, -22941, -24131, -25255, -26311, -27295, -28204, -29036, -29789, -30461, -31050, -31554, -31972, -32303, -32546, -32700, -32765, -32740, -32626, -32424, -32132, -31753, -31288, -30737, -30103, -29386, -28589, -27714, -26764, -25740, -24647, -23486, -22261, -20975, -19632, -18236, -16790, -15298, -13764, -12193, -10589, -8955, -7298, -5620, -3927, -2223}}, + {203U, 118U, {0, 1745, 3485, 5215, 6930, 8626, 10297, 11939, 13547, 15117, 16643, 18123, 19551, 20923, 22237, 23487, 24670, 25784, 26824, 27788, 28673, 29477, 30198, 30832, 31379, 31837, 32205, 32481, 32666, 32757, 32756, 32661, 32474, 32195, 31824, 31363, 30813, 30176, 29453, 28646, 27758, 26792, 25749, 24633, 23448, 22196, 20880, 19506, 18076, 16595, 15067, 13496, 11887, 10244, 8572, 6876, 5160, 3429, 1689, -55, -1800, -3539, -5269, -6984, -8679, -10349, -11990, -13597, -15165, -16690, -18168, -19595, -20965, -22277, -23525, -24706, -25817, -26855, -27817, -28699, -29501, -30218, -30850, -31394, -31850, -32214, -32488, -32669, -32757, -32753, -32656, -32466, -32183, -31810, -31346, -30793, -30153, -29427, -28618, -27728, -26759, -25714, -24596, -23408, -22154, -20836, -19460, -18029, -16546, -15017, -13444, -11834, -10190, -8517, -6820, -5104, -3373, -1633}}, + {206U, 116U, {0, 1771, 3536, 5291, 7031, 8750, 10444, 12107, 13734, 15322, 16865, 18358, 19798, 21180, 22500, 23754, 24939, 26051, 27087, 28044, 28919, 29709, 30413, 31028, 31552, 31984, 32322, 32566, 32715, 32768, 32725, 32587, 32354, 32026, 31604, 31091, 30486, 29792, 29011, 28146, 27198, 26171, 25067, 23890, 22643, 21330, 19955, 18521, 17033, 15496, 13913, 12290, 10630, 8940, 7223, 5486, 3732, 1968, 197, -1573, -3339, -5095, -6837, -8559, -10255, -11922, -13554, -15146, -16694, -18193, -19639, -21028, -22355, -23617, -24810, -25930, -26975, -27941, -28825, -29625, -30338, -30963, -31497, -31939, -32288, -32542, -32702, -32766, -32734, -32606, -32383, -32066, -31655, -31151, -30557, -29873, -29101, -28245, -27306, -26288, -25192, -24023, -22784, -21478, -20110, -18683, -17201, -15668, -14090, -12471, -10816, -9129, -7415, -5679, -3927, -2164}}, + {210U, 114U, {0, 1807, 3608, 5398, 7172, 8923, 10648, 12341, 13995, 15608, 17172, 18685, 20141, 21535, 22864, 24123, 25309, 26418, 27447, 28392, 29251, 30021, 30699, 31284, 31774, 32167, 32463, 32659, 32757, 32754, 32652, 32451, 32151, 31753, 31259, 30669, 29986, 29212, 28350, 27400, 26368, 25255, 24066, 22803, 21471, 20074, 18615, 17100, 15533, 13919, 12262, 10568, 8842, 7089, 5314, 3523, 1722, -84, -1890, -3691, -5481, -7253, -9004, -10727, -12418, -14071, -15681, -17244, -18754, -20207, -21598, -22924, -24180, -25362, -26467, -27492, -28433, -29288, -30054, -30728, -31308, -31794, -32182, -32473, -32665, -32758, -32751, -32644, -32438, -32134, -31731, -31232, -30638, -29951, -29173, -28306, -27353, -26316, -25200, -24007, -22741, -21406, -20005, -18544, -17027, -15457, -13841, -12182, -10486, -8759, -7005, -5229, -3438, -1636}}, + {218U, 110U, {0, 1870, 3734, 5586, 7419, 9228, 11008, 12751, 14453, 16108, 17710, 19254, 20736, 22150, 23492, 24758, 25942, 27042, 28054, 28975, 29801, 30530, 31159, 31687, 32112, 32432, 32646, 32754, 32755, 32650, 32437, 32120, 31697, 31171, 30544, 29817, 28993, 28074, 27064, 25966, 24783, 23519, 22179, 20766, 19286, 17742, 16141, 14488, 12787, 11044, 9265, 7457, 5624, 3772, 1909, 39, -1830, -3695, -5547, -7381, -9190, -10970, -12715, -14417, -16073, -17676, -19222, -20705, -22121, -23464, -24731, -25918, -27020, -28033, -28956, -29784, -30515, -31146, -31677, -32103, -32425, -32642, -32752, -32755, -32652, -32442, -32126, -31706, -31182, -30557, -29832, -29010, -28093, -27085, -25988, -24807, -23545, -22206, -20795, -19316, -17774, -16174, -14521, -12821, -11079, -9302, -7493, -5661, -3810, -1946}}, + {225U, 106U, {0, 1935, 3863, 5778, 7673, 9541, 11375, 13170, 14919, 16616, 18255, 19830, 21336, 22767, 24119, 25387, 26566, 27653, 28643, 29533, 30319, 31000, 31573, 32036, 32387, 32625, 32748, 32758, 32653, 32435, 32103, 31659, 31104, 30441, 29672, 28799, 27826, 26755, 25591, 24338, 23000, 21582, 20088, 18524, 16895, 15208, 13467, 11680, 9851, 7989, 6098, 4186, 2260, 325, -1609, -3539, -5457, -7355, -9228, -11069, -12871, -14628, -16334, -17983, -19569, -21087, -22531, -23897, -25179, -26374, -27476, -28482, -29389, -30194, -30893, -31484, -31965, -32335, -32592, -32735, -32763, -32678, -32478, -32165, -31740, -31204, -30559, -29807, -28952, -27995, -26941, -25792, -24554, -23229, -21824, -20343, -18790, -17172, -15494, -13762, -11982, -10160, -8303, -6416, -4507, -2583}}, + {229U, 105U, {0, 1964, 3921, 5864, 7786, 9680, 11539, 13357, 15127, 16842, 18496, 20084, 21600, 23038, 24394, 25661, 26837, 27916, 28894, 29768, 30536, 31194, 31739, 32170, 32486, 32685, 32766, 32729, 32575, 32304, 31916, 31413, 30798, 30072, 29238, 28298, 27257, 26118, 24885, 23562, 22155, 20668, 19106, 17476, 15783, 14033, 12233, 10389, 8507, 6595, 4660, 2707, 745, -1219, -3180, -5129, -7060, -8965, -10838, -12673, -14461, -16198, -17876, -19490, -21034, -22502, -23889, -25191, -26402, -27517, -28534, -29449, -30257, -30956, -31545, -32019, -32379, -32622, -32748, -32756, -32646, -32419, -32075, -31616, -31043, -30359, -29565, -28665, -27662, -26560, -25362, -24072, -22696, -21239, -19705, -18100, -16431, -14702, -12920, -11091, -9223, -7322, -5394, -3447, -1488}}, + {233U, 103U, {0, 2003, 3998, 5978, 7936, 9864, 11756, 13603, 15400, 17139, 18814, 20418, 21946, 23393, 24751, 26017, 27186, 28254, 29215, 30068, 30807, 31432, 31939, 32327, 32594, 32739, 32762, 32662, 32440, 32097, 31634, 31052, 30354, 29543, 28622, 27593, 26461, 25231, 23906, 22491, 20993, 19416, 17766, 16050, 14274, 12445, 10569, 8653, 6705, 4733, 2742, 741, -1261, -3260, -5247, -7214, -9154, -11060, -12925, -14741, -16502, -18201, -19832, -21389, -22867, -24258, -25559, -26765, -27870, -28871, -29764, -30546, -31214, -31765, -32197, -32509, -32699, -32767, -32712, -32535, -32237, -31818, -31280, -30625, -29855, -28974, -27985, -26891, -25696, -24405, -23023, -21555, -20007, -18383, -16691, -14936, -13126, -11266, -9365, -7428, -5464, -3479, -1481}}, + {241U, 99U, {0, 2073, 4138, 6186, 8209, 10199, 12149, 14050, 15894, 17675, 19385, 21018, 22566, 24024, 25385, 26645, 27799, 28840, 29767, 30574, 31258, 31818, 32250, 32552, 32725, 32766, 32676, 32455, 32104, 31624, 31018, 30287, 29436, 28466, 27382, 26189, 24890, 23492, 22000, 20420, 18757, 17020, 15215, 13348, 11428, 9463, 7459, 5426, 3371, 1302, -771, -2842, -4901, -6941, -8953, -10929, -12861, -14742, -16564, -18319, -20001, -21603, -23118, -24541, -25866, -27086, -28199, -29198, -30080, -30842, -31480, -31992, -32376, -32631, -32754, -32747, -32608, -32338, -31939, -31412, -30759, -29983, -29087, -28075, -26949, -25716, -24380, -22946, -21421, -19809, -18118, -16355, -14526, -12639, -10701, -8720, -6704, -4662, -2601}}, + {250U, 96U, {0, 2146, 4282, 6400, 8491, 10545, 12554, 14509, 16402, 18224, 19968, 21627, 23192, 24658, 26019, 27267, 28398, 29408, 30291, 31045, 31665, 32149, 32495, 32702, 32768, 32694, 32479, 32125, 31633, 31005, 30244, 29353, 28337, 27198, 25943, 24577, 23105, 21534, 19870, 18121, 16295, 14398, 12440, 10428, 8372, 6279, 4160, 2022, -123, -2268, -4404, -6520, -8609, -10661, -12667, -14619, -16508, -18326, -20065, -21718, -23278, -24739, -26092, -27334, -28459, -29461, -30337, -31083, -31695, -32172, -32510, -32708, -32766, -32684, -32461, -32099, -31599, -30964, -30195, -29297, -28273, -27128, -25867, -24494, -23016, -21439, -19771, -18017, -16186, -14286, -12324, -10310, -8251, -6157, -4036, -1898}}, + {254U, 94U, {0, 2178, 4347, 6496, 8617, 10699, 12734, 14713, 16627, 18467, 20226, 21895, 23467, 24935, 26293, 27535, 28655, 29648, 30510, 31237, 31826, 32274, 32580, 32741, 32757, 32629, 32356, 31940, 31382, 30686, 29854, 28890, 27799, 26584, 25251, 23807, 22258, 20610, 18871, 17049, 15151, 13186, 11163, 9090, 6977, 4834, 2668, 492, -1687, -3858, -6013, -8141, -10233, -12279, -14271, -16201, -18058, -19836, -21526, -23120, -24613, -25996, -27265, -28412, -29435, -30327, -31084, -31705, -32185, -32522, -32716, -32765, -32669, -32429, -32045, -31519, -30854, -30053, -29118, -28055, -26867, -25561, -24141, -22615, -20989, -19270, -17466, -15584, -13633, -11623, -9560, -7456, -5318, -3157}} +}; +const uint8_t CTCSS_TABLE_LEN = 50U; + CFMCTCSSTX::CFMCTCSSTX() : -m_level(128 * 128) +m_entry(NULL), +m_level(128 * 128), +m_n(0U) { } void CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) { + for (uint8_t i = 0U; i < CTCSS_TABLE_LEN; i++) { + if (CTCSS_TONES[i].m_frequency == frequency) { + m_entry = CTCSS_TONES + i; + break; + } + } + m_level = q15_t(level * 128); } void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) { + if (m_entry == NULL) + return; + + for (uint8_t i = 0U; i < length; i++) { + q31_t sample = m_entry->m_values[m_n] * m_level; + samples[i] = q15_t(__SSAT((sample >> 15), 16)); + + m_n++; + if (m_n >= m_entry->m_length) + m_n = 0U; + } } diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index 906b363..1f2f27a 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -21,6 +21,12 @@ #include "Config.h" +struct CTCSS_TABLE { + uint8_t m_frequency; + uint16_t m_length; + q15_t m_values[360U]; +}; + class CFMCTCSSTX { public: CFMCTCSSTX(); @@ -30,7 +36,9 @@ public: void getAudio(q15_t* samples, uint8_t length); private: - q15_t m_level; + CTCSS_TABLE* m_entry; + q15_t m_level; + uint16_t m_n; }; #endif From 4b0c3d88f3aee992dc567da28892b2b53798ce6e Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 15 Apr 2020 22:41:30 +0100 Subject: [PATCH 014/119] More work on the keyer. --- FM.cpp | 8 +++++++- FMKeyer.cpp | 29 +++++++++++++++++++++++++++-- FMKeyer.h | 8 +++++--- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 41826e2..5f2e217 100644 --- a/FM.cpp +++ b/FM.cpp @@ -89,7 +89,13 @@ void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, u m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; - m_holdoffTimer.setTimeout(holdoff, 0U); + uint16_t holdoffTime = 0U; + uint16_t callsignTime = time * 60U; + if (holdoff > 0U) + holdoffTime = callsignTime / holdoff; + + m_holdoffTimer.setTimeout(holdoffTime, 0U); + m_callsignTimer.setTimeout(callsignTime, 0U); } void CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 88f3fff..3c9ec1f 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -21,7 +21,7 @@ #include "FMKeyer.h" const struct { - uint8_t c; + char c; uint32_t pattern; uint8_t length; } SYMBOL_LIST[] = { @@ -78,17 +78,40 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02 CFMKeyer::CFMKeyer() : m_level(128 * 128), m_wanted(false), -m_running(false) +m_running(false), +m_poBuffer(), +m_poLen(0U) { } void CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) { m_level = q15_t(level * 128); + + for (uint8_t i = 0U; text[i] != '\0'; i++) { + for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { + if (SYMBOL_LIST[j].c == text[i]) { + uint32_t MASK = 0x80000000U; + for (uint8_t k = 0U; k < SYMBOL_LIST[j].length; k++, m_poLen++, MASK >>= 1) { + bool b = (SYMBOL_LIST[j].pattern & MASK) == MASK; + WRITE_BIT(m_poBuffer, m_poLen, b); + + if (m_poLen >= 995U) { + m_poLen = 0U; + return; + } + } + + break; + } + } + } } void CFMKeyer::getAudio(q15_t* samples, uint8_t length) { + if (!m_wanted && !m_running) + return; } void CFMKeyer::start() @@ -98,6 +121,8 @@ void CFMKeyer::start() void CFMKeyer::stop() { + m_wanted = false; + m_running = false; } bool CFMKeyer::isRunning() const diff --git a/FMKeyer.h b/FMKeyer.h index e0b3ca8..e34e17f 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -35,9 +35,11 @@ public: bool isRunning() const; private: - q15_t m_level; - bool m_wanted; - bool m_running; + q15_t m_level; + bool m_wanted; + bool m_running; + uint8_t m_poBuffer[1000U]; + uint16_t m_poLen; }; #endif From bbe56f5082702d91b7c53fbaaa730ba9fcfb261f Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 16 Apr 2020 14:00:31 +0100 Subject: [PATCH 015/119] Return configuration errors. --- FM.cpp | 24 +++++++++++++----------- FM.h | 6 +++--- FMCTCSSTX.cpp | 4 +++- FMCTCSSTX.h | 2 +- FMKeyer.cpp | 6 ++++-- FMKeyer.h | 2 +- SerialPort.cpp | 12 +++--------- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/FM.cpp b/FM.cpp index 5f2e217..c455858 100644 --- a/FM.cpp +++ b/FM.cpp @@ -82,10 +82,8 @@ void CFM::reset() { } -void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd) +uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd) { - m_callsign.setParams(callsign, speed, frequency, lowLevel); - m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; @@ -96,25 +94,29 @@ void CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, u m_holdoffTimer.setTimeout(holdoffTime, 0U); m_callsignTimer.setTimeout(callsignTime, 0U); + + return m_callsign.setParams(callsign, speed, frequency, lowLevel); } -void CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) +uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) { - m_rfAck.setParams(rfAck, speed, frequency, level); - m_ackDelayTimer.setTimeout(0U, delay); m_ackMinTimer.setTimeout(minTime, 0U); + + return m_rfAck.setParams(rfAck, speed, frequency, level); } -void CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) { - m_timeoutTone.setParams(timeoutLevel); - m_goertzel.setParams(ctcssFrequency, ctcssThreshold); - m_ctcss.setParams(ctcssFrequency, ctcssLevel); - m_timeoutTimer.setTimeout(timeout, 0U); m_kerchunkTimer.setTimeout(kerchunkTime, 0U); m_hangTimer.setTimeout(hangTime, 0U); + + m_timeoutTone.setParams(timeoutLevel); + + m_goertzel.setParams(ctcssFrequency, ctcssThreshold); + + return m_ctcss.setParams(ctcssFrequency, ctcssLevel); } void CFM::stateMachine(bool validSignal, uint8_t length) diff --git a/FM.h b/FM.h index eab9450..ef5aac5 100644 --- a/FM.h +++ b/FM.h @@ -47,9 +47,9 @@ public: void reset(); - void setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); - void setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - void setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); + uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); + uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: CFMKeyer m_callsign; diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 0d8982a..737d1a3 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -81,7 +81,7 @@ m_n(0U) { } -void CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) +uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) { for (uint8_t i = 0U; i < CTCSS_TABLE_LEN; i++) { if (CTCSS_TONES[i].m_frequency == frequency) { @@ -91,6 +91,8 @@ void CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) } m_level = q15_t(level * 128); + + return m_entry == NULL ? 4U : 0U; } void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index 1f2f27a..5eee1c4 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -31,7 +31,7 @@ class CFMCTCSSTX { public: CFMCTCSSTX(); - void setParams(uint8_t frequency, uint8_t level); + uint8_t setParams(uint8_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 3c9ec1f..56aa3d3 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -84,7 +84,7 @@ m_poLen(0U) { } -void CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) +uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) { m_level = q15_t(level * 128); @@ -98,7 +98,7 @@ void CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, ui if (m_poLen >= 995U) { m_poLen = 0U; - return; + return 4U; } } @@ -106,6 +106,8 @@ void CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, ui } } } + + return 0U; } void CFMKeyer::getAudio(q15_t* samples, uint8_t length) diff --git a/FMKeyer.h b/FMKeyer.h index e34e17f..dbcb011 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -25,7 +25,7 @@ class CFMKeyer { public: CFMKeyer(); - void setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level); + uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); diff --git a/SerialPort.cpp b/SerialPort.cpp index 0d81a09..7256571 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -384,9 +384,7 @@ uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) callsign[n] = data[i]; callsign[n] = '\0'; - fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd); - - return 0U; + return fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd); } uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) @@ -406,9 +404,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) ack[n] = data[i]; ack[n] = '\0'; - fm.setAck(ack, speed, frequency, minTime, delay, level); - - return 0U; + return fm.setAck(ack, speed, frequency, minTime, delay, level); } uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) @@ -426,9 +422,7 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t kerchunkTime = data[5U]; uint8_t hangTime = data[6U]; - fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); - - return 0U; + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From 4a5f985d027c265471a5f1bea68aa339d27a106f Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 16 Apr 2020 14:27:33 +0100 Subject: [PATCH 016/119] More efficient CTCSS tone generation. --- FMCTCSSTX.cpp | 142 ++++++++++++++++++++++++++++---------------------- FMCTCSSTX.h | 12 ++--- 2 files changed, 82 insertions(+), 72 deletions(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 737d1a3..0513f13 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -20,92 +20,108 @@ #include "Globals.h" #include "FMCTCSSTX.h" -const CTCSS_TABLE CTCSS_TONES[] = { - {67U, 358U, {0, 575, 1149, 1724, 2297, 2870, 3442, 4013, 4583, 5151, 5718, 6283, 6846, 7407, 7966, 8522, 9076, 9627, 10175, 10720, 11261, 11799, 12333, 12864, 13391, 13913, 14431, 14945, 15454, 15959, 16458, 16953, 17442, 17926, 18404, 18877, 19344, 19805, 20260, 20708, 21150, 21586, 22015, 22437, 22853, 23261, 23663, 24056, 24443, 24822, 25193, 25557, 25913, 26261, 26600, 26932, 27255, 27570, 27876, 28174, 28463, 28744, 29015, 29278, 29531, 29776, 30011, 30237, 30454, 30662, 30860, 31048, 31227, 31397, 31556, 31706, 31847, 31977, 32098, 32208, 32309, 32400, 32481, 32552, 32613, 32664, 32704, 32735, 32756, 32766, 32767, 32757, 32738, 32708, 32668, 32619, 32559, 32489, 32409, 32319, 32219, 32110, 31990, 31861, 31721, 31572, 31414, 31245, 31067, 30880, 30683, 30476, 30261, 30035, 29801, 29557, 29305, 29043, 28772, 28493, 28205, 27908, 27602, 27288, 26966, 26635, 26296, 25950, 25595, 25232, 24861, 24483, 24097, 23704, 23304, 22896, 22481, 22060, 21631, 21196, 20755, 20307, 19852, 19392, 18926, 18454, 17976, 17493, 17004, 16510, 16011, 15507, 14999, 14485, 13967, 13445, 12919, 12389, 11855, 11317, 10776, 10232, 9684, 9134, 8580, 8024, 7466, 6905, 6342, 5777, 5211, 4643, 4073, 3502, 2930, 2357, 1783, 1209, 635, 60, -514, -1088, -1663, -2236, -2809, -3382, -3953, -4523, -5091, -5658, -6223, -6787, -7348, -7907, -8463, -9017, -9568, -10117, -10662, -11204, -11742, -12277, -12808, -13335, -13858, -14376, -14891, -15400, -15905, -16405, -16900, -17390, -17875, -18353, -18827, -19294, -19756, -20211, -20661, -21103, -21540, -21970, -22393, -22809, -23218, -23620, -24015, -24402, -24782, -25154, -25518, -25875, -26224, -26564, -26897, -27221, -27536, -27844, -28142, -28433, -28714, -28986, -29250, -29504, -29750, -29986, -30213, -30431, -30639, -30838, -31028, -31208, -31378, -31539, -31690, -31831, -31963, -32084, -32196, -32298, -32390, -32472, -32544, -32606, -32658, -32700, -32731, -32753, -32765, -32766, -32758, -32739, -32711, -32672, -32623, -32564, -32496, -32417, -32328, -32229, -32121, -32002, -31874, -31735, -31587, -31430, -31262, -31085, -30899, -30703, -30497, -30283, -30058, -29825, -29582, -29331, -29070, -28800, -28522, -28234, -27938, -27634, -27321, -26999, -26669, -26331, -25985, -25631, -25269, -24899, -24522, -24137, -23744, -23345, -22938, -22524, -22103, -21675, -21241, -20800, -20353, -19899, -19440, -18974, -18502, -18025, -17542, -17054, -16561, -16063, -15559, -15051, -14538, -14021, -13499, -12973, -12444, -11910, -11373, -10832, -10288, -9741, -9190, -8637, -8082, -7523, -6963, -6400, -5835, -5269, -4701, -4131, -3561, -2989, -2416, -1842, -1268, -694}}, - {69U, 346U, {0, 594, 1189, 1783, 2376, 2968, 3560, 4150, 4739, 5327, 5912, 6496, 7078, 7657, 8234, 8808, 9379, 9947, 10512, 11073, 11631, 12185, 12734, 13280, 13821, 14358, 14890, 15417, 15939, 16456, 16967, 17473, 17973, 18467, 18955, 19437, 19912, 20381, 20843, 21299, 21747, 22188, 22622, 23048, 23467, 23878, 24281, 24676, 25063, 25442, 25813, 26175, 26528, 26873, 27208, 27535, 27853, 28161, 28461, 28751, 29031, 29302, 29563, 29815, 30057, 30288, 30510, 30722, 30924, 31115, 31297, 31468, 31628, 31778, 31918, 32047, 32166, 32274, 32372, 32459, 32535, 32600, 32655, 32699, 32732, 32755, 32766, 32767, 32757, 32736, 32705, 32663, 32610, 32546, 32472, 32386, 32291, 32184, 32067, 31940, 31802, 31653, 31494, 31325, 31145, 30955, 30755, 30545, 30325, 30094, 29854, 29604, 29345, 29075, 28796, 28508, 28210, 27903, 27587, 27261, 26927, 26584, 26232, 25871, 25502, 25125, 24739, 24345, 23943, 23533, 23116, 22691, 22258, 21818, 21371, 20917, 20456, 19988, 19514, 19033, 18546, 18053, 17554, 17049, 16538, 16022, 15501, 14975, 14444, 13908, 13367, 12822, 12273, 11720, 11163, 10602, 10038, 9470, 8900, 8326, 7750, 7171, 6589, 6006, 5421, 4834, 4245, 3655, 3063, 2471, 1878, 1284, 690, 95, -498, -1093, -1687, -2280, -2873, -3464, -4055, -4644, -5232, -5818, -6402, -6984, -7563, -8141, -8715, -9287, -9855, -10421, -10982, -11541, -12095, -12646, -13192, -13734, -14271, -14804, -15332, -15855, -16373, -16885, -17392, -17892, -18388, -18877, -19359, -19836, -20306, -20769, -21225, -21675, -22117, -22552, -22979, -23399, -23812, -24216, -24613, -25001, -25381, -25753, -26116, -26471, -26817, -27154, -27482, -27802, -28112, -28412, -28704, -28986, -29258, -29521, -29774, -30018, -30251, -30474, -30688, -30891, -31084, -31267, -31440, -31602, -31754, -31896, -32026, -32147, -32257, -32356, -32444, -32522, -32589, -32646, -32692, -32727, -32751, -32764, -32767, -32758, -32739, -32710, -32669, -32618, -32556, -32483, -32400, -32306, -32201, -32086, -31960, -31823, -31677, -31519, -31352, -31174, -30985, -30787, -30578, -30360, -30131, -29892, -29644, -29386, -29118, -28841, -28554, -28257, -27952, -27637, -27313, -26980, -26638, -26288, -25929, -25561, -25185, -24800, -24408, -24007, -23599, -23182, -22758, -22327, -21888, -21442, -20989, -20529, -20062, -19589, -19109, -18623, -18131, -17633, -17129, -16619, -16104, -15584, -15059, -14528, -13993, -13453, -12909, -12360, -11808, -11251, -10691, -10127, -9560, -8990, -8417, -7841, -7263, -6682, -6099, -5514, -4927, -4338, -3748, -3157, -2565, -1972, -1378, -784}}, - {71U, 334U, {0, 617, 1233, 1849, 2465, 3079, 3693, 4305, 4916, 5525, 6132, 6736, 7339, 7939, 8536, 9130, 9720, 10308, 10891, 11471, 12047, 12618, 13185, 13747, 14305, 14857, 15404, 15946, 16482, 17012, 17536, 18054, 18566, 19071, 19569, 20060, 20544, 21021, 21490, 21952, 22406, 22852, 23290, 23720, 24141, 24554, 24958, 25353, 25740, 26117, 26485, 26843, 27192, 27531, 27861, 28181, 28490, 28790, 29080, 29359, 29627, 29886, 30133, 30370, 30596, 30812, 31016, 31210, 31392, 31563, 31724, 31872, 32010, 32136, 32251, 32354, 32446, 32527, 32596, 32653, 32699, 32733, 32756, 32767, 32766, 32754, 32730, 32695, 32648, 32589, 32519, 32438, 32344, 32240, 32124, 31997, 31858, 31708, 31547, 31374, 31191, 30996, 30790, 30574, 30347, 30109, 29860, 29601, 29331, 29051, 28760, 28459, 28149, 27828, 27497, 27157, 26807, 26448, 26079, 25701, 25314, 24918, 24513, 24099, 23677, 23246, 22807, 22360, 21906, 21443, 20973, 20495, 20010, 19518, 19020, 18514, 18002, 17483, 16959, 16428, 15891, 15349, 14801, 14249, 13691, 13128, 12560, 11988, 11412, 10832, 10248, 9661, 9069, 8475, 7878, 7278, 6675, 6070, 5463, 4854, 4243, 3631, 3017, 2402, 1787, 1171, 554, -62, -678, -1295, -1911, -2526, -3141, -3754, -4366, -4977, -5585, -6192, -6797, -7399, -7998, -8595, -9189, -9779, -10366, -10949, -11529, -12104, -12675, -13241, -13803, -14360, -14912, -15459, -16000, -16535, -17065, -17588, -18105, -18616, -19120, -19618, -20108, -20592, -21068, -21537, -21998, -22451, -22896, -23333, -23762, -24183, -24595, -24998, -25392, -25777, -26154, -26520, -26878, -27226, -27564, -27893, -28212, -28520, -28819, -29107, -29385, -29653, -29910, -30157, -30393, -30618, -30832, -31035, -31228, -31409, -31579, -31738, -31886, -32022, -32147, -32261, -32363, -32454, -32533, -32601, -32657, -32702, -32735, -32756, -32766, -32765, -32751, -32726, -32690, -32641, -32582, -32510, -32428, -32333, -32228, -32110, -31982, -31842, -31691, -31529, -31355, -31170, -30975, -30768, -30550, -30322, -30083, -29833, -29573, -29302, -29021, -28729, -28427, -28116, -27794, -27462, -27121, -26770, -26410, -26040, -25661, -25273, -24876, -24470, -24055, -23632, -23201, -22761, -22314, -21858, -21395, -20924, -20445, -19960, -19467, -18968, -18461, -17948, -17429, -16904, -16373, -15835, -15293, -14745, -14191, -13633, -13069, -12502, -11929, -11353, -10772, -10188, -9600, -9008, -8414, -7816, -7216, -6613, -6008, -5400, -4791, -4180, -3567, -2954, -2339, -1723, -1107, -491}}, - {74U, 323U, {0, 638, 1276, 1914, 2550, 3186, 3821, 4454, 5085, 5715, 6342, 6967, 7589, 8209, 8825, 9438, 10048, 10653, 11255, 11852, 12445, 13033, 13616, 14194, 14766, 15333, 15894, 16449, 16998, 17541, 18076, 18605, 19127, 19642, 20149, 20648, 21140, 21623, 22099, 22566, 23024, 23474, 23915, 24347, 24769, 25182, 25586, 25980, 26364, 26738, 27102, 27455, 27799, 28131, 28453, 28764, 29065, 29354, 29632, 29899, 30154, 30398, 30631, 30852, 31061, 31258, 31444, 31617, 31779, 31929, 32066, 32191, 32305, 32405, 32494, 32570, 32634, 32685, 32725, 32751, 32765, 32767, 32757, 32734, 32698, 32650, 32590, 32517, 32432, 32335, 32226, 32104, 31970, 31824, 31666, 31495, 31313, 31119, 30914, 30696, 30467, 30226, 29974, 29710, 29436, 29150, 28853, 28545, 28226, 27896, 27556, 27205, 26845, 26474, 26092, 25701, 25301, 24890, 24470, 24041, 23603, 23156, 22700, 22235, 21762, 21281, 20792, 20295, 19790, 19277, 18757, 18231, 17697, 17156, 16609, 16056, 15497, 14931, 14360, 13784, 13202, 12616, 12024, 11428, 10828, 10224, 9615, 9003, 8388, 7770, 7148, 6524, 5897, 5268, 4637, 4005, 3371, 2735, 2099, 1461, 823, 185, -452, -1090, -1728, -2365, -3001, -3636, -4269, -4901, -5531, -6159, -6785, -7408, -8028, -8646, -9260, -9870, -10477, -11079, -11678, -12272, -12861, -13446, -14025, -14600, -15168, -15731, -16288, -16838, -17383, -17920, -18451, -18975, -19492, -20001, -20503, -20997, -21483, -21961, -22430, -22891, -23344, -23787, -24221, -24647, -25062, -25469, -25866, -26252, -26629, -26996, -27353, -27699, -28035, -28360, -28674, -28977, -29270, -29551, -29821, -30080, -30328, -30563, -30788, -31000, -31201, -31390, -31567, -31732, -31885, -32026, -32155, -32272, -32376, -32468, -32548, -32616, -32671, -32713, -32744, -32762, -32767, -32760, -32741, -32709, -32664, -32608, -32539, -32457, -32364, -32258, -32139, -32009, -31866, -31712, -31545, -31366, -31176, -30974, -30759, -30534, -30296, -30047, -29787, -29516, -29233, -28939, -28634, -28318, -27992, -27655, -27307, -26949, -26581, -26203, -25815, -25417, -25009, -24592, -24166, -23730, -23286, -22832, -22370, -21900, -21421, -20934, -20439, -19936, -19426, -18908, -18383, -17851, -17313, -16768, -16216, -15659, -15095, -14526, -13951, -13371, -12786, -12196, -11601, -11002, -10399, -9791, -9181, -8566, -7948, -7328, -6704, -6078, -5450, -4820, -4188, -3554, -2919, -2282, -1645, -1008, -370}}, - {77U, 312U, {0, 661, 1321, 1980, 2639, 3297, 3954, 4609, 5262, 5912, 6561, 7207, 7850, 8489, 9126, 9758, 10387, 11011, 11631, 12246, 12856, 13461, 14061, 14654, 15242, 15824, 16399, 16967, 17529, 18083, 18631, 19170, 19702, 20226, 20741, 21248, 21747, 22237, 22717, 23189, 23651, 24103, 24546, 24978, 25401, 25813, 26214, 26605, 26986, 27355, 27713, 28060, 28395, 28719, 29031, 29332, 29620, 29897, 30161, 30413, 30653, 30880, 31095, 31297, 31486, 31663, 31826, 31977, 32115, 32239, 32351, 32450, 32535, 32607, 32666, 32711, 32743, 32762, 32768, 32760, 32739, 32705, 32657, 32596, 32522, 32435, 32335, 32221, 32094, 31954, 31802, 31636, 31457, 31266, 31062, 30845, 30616, 30374, 30120, 29854, 29576, 29286, 28983, 28669, 28344, 28006, 27658, 27298, 26927, 26545, 26152, 25749, 25335, 24911, 24477, 24033, 23579, 23116, 22643, 22161, 21670, 21170, 20662, 20145, 19620, 19087, 18546, 17998, 17442, 16879, 16310, 15733, 15151, 14562, 13967, 13367, 12761, 12150, 11535, 10914, 10289, 9660, 9027, 8390, 7750, 7106, 6460, 5811, 5160, 4507, 3851, 3195, 2537, 1878, 1218, 558, -102, -762, -1423, -2082, -2741, -3399, -4055, -4709, -5362, -6013, -6661, -7306, -7948, -8588, -9223, -9855, -10483, -11107, -11726, -12340, -12950, -13554, -14152, -14745, -15332, -15913, -16487, -17054, -17615, -18168, -18714, -19253, -19783, -20306, -20820, -21326, -21823, -22311, -22790, -23260, -23721, -24172, -24613, -25044, -25465, -25875, -26275, -26664, -27043, -27410, -27767, -28112, -28445, -28767, -29078, -29376, -29663, -29938, -30200, -30450, -30688, -30913, -31126, -31326, -31513, -31688, -31850, -31998, -32134, -32257, -32366, -32463, -32546, -32616, -32673, -32716, -32746, -32763, -32767, -32757, -32734, -32697, -32648, -32585, -32509, -32419, -32317, -32201, -32072, -31930, -31776, -31608, -31427, -31234, -31028, -30809, -30578, -30335, -30079, -29811, -29530, -29238, -28934, -28618, -28291, -27952, -27601, -27240, -26867, -26484, -26089, -25684, -25269, -24843, -24408, -23962, -23507, -23042, -22567, -22084, -21592, -21090, -20581, -20062, -19536, -19002, -18460, -17910, -17354, -16790, -16219, -15642, -15059, -14469, -13873, -13272, -12665, -12054, -11437, -10816, -10190, -9560, -8927, -8289, -7649, -7005, -6358, -5709, -5057, -4404, -3748, -3091, -2433, -1774, -1114, -454}}, - {79U, 301U, {0, 684, 1367, 2050, 2732, 3412, 4092, 4769, 5444, 6117, 6788, 7455, 8119, 8780, 9436, 10089, 10737, 11381, 12020, 12653, 13281, 13903, 14519, 15129, 15732, 16328, 16917, 17499, 18073, 18640, 19198, 19748, 20289, 20822, 21345, 21859, 22364, 22858, 23343, 23818, 24282, 24736, 25179, 25611, 26032, 26442, 26840, 27226, 27601, 27963, 28313, 28651, 28977, 29290, 29590, 29877, 30151, 30413, 30661, 30895, 31116, 31324, 31518, 31698, 31864, 32017, 32155, 32280, 32390, 32487, 32569, 32637, 32691, 32731, 32756, 32767, 32764, 32747, 32715, 32669, 32609, 32535, 32447, 32344, 32227, 32097, 31952, 31793, 31621, 31435, 31235, 31022, 30795, 30554, 30301, 30034, 29754, 29461, 29155, 28837, 28506, 28162, 27807, 27439, 27059, 26668, 26265, 25850, 25424, 24988, 24540, 24081, 23613, 23133, 22644, 22145, 21636, 21118, 20591, 20055, 19510, 18956, 18394, 17824, 17247, 16662, 16069, 15470, 14864, 14252, 13633, 13008, 12378, 11742, 11101, 10456, 9806, 9151, 8493, 7830, 7165, 6496, 5825, 5151, 4474, 3796, 3116, 2435, 1753, 1070, 386, -297, -980, -1663, -2346, -3027, -3707, -4386, -5062, -5737, -6409, -7078, -7744, -8406, -9065, -9720, -10371, -11017, -11659, -12295, -12926, -13551, -14171, -14784, -15391, -15991, -16585, -17171, -17749, -18320, -18883, -19437, -19984, -20521, -21050, -21569, -22079, -22579, -23070, -23550, -24020, -24480, -24929, -25368, -25795, -26211, -26615, -27008, -27390, -27759, -28116, -28461, -28794, -29114, -29421, -29716, -29997, -30266, -30521, -30763, -30992, -31207, -31409, -31597, -31771, -31931, -32078, -32210, -32329, -32433, -32523, -32600, -32661, -32709, -32743, -32762, -32767, -32758, -32734, -32696, -32644, -32578, -32497, -32403, -32294, -32171, -32034, -31884, -31719, -31541, -31349, -31143, -30923, -30691, -30444, -30185, -29912, -29627, -29328, -29017, -28693, -28357, -28008, -27647, -27274, -26889, -26493, -26085, -25665, -25235, -24793, -24341, -23878, -23404, -22921, -22427, -21924, -21411, -20889, -20357, -19817, -19269, -18711, -18146, -17573, -16992, -16404, -15808, -15206, -14597, -13982, -13361, -12733, -12101, -11463, -10820, -10172, -9520, -8864, -8204, -7540, -6873, -6203, -5530, -4855, -4178, -3499, -2819, -2137, -1454, -771}}, - {82U, 291U, {0, 708, 1415, 2122, 2827, 3532, 4235, 4935, 5634, 6330, 7022, 7712, 8398, 9080, 9758, 10431, 11100, 11763, 12421, 13073, 13719, 14358, 14991, 15617, 16235, 16846, 17449, 18044, 18631, 19208, 19777, 20337, 20887, 21428, 21958, 22478, 22988, 23487, 23975, 24452, 24917, 25371, 25813, 26243, 26660, 27066, 27458, 27838, 28205, 28558, 28899, 29226, 29539, 29838, 30124, 30395, 30653, 30896, 31124, 31338, 31538, 31722, 31892, 32047, 32188, 32313, 32423, 32518, 32597, 32662, 32711, 32745, 32764, 32767, 32756, 32729, 32686, 32629, 32556, 32468, 32365, 32246, 32113, 31965, 31802, 31624, 31431, 31223, 31001, 30765, 30514, 30249, 29970, 29677, 29370, 29049, 28715, 28367, 28006, 27632, 27246, 26846, 26434, 26010, 25573, 25125, 24665, 24193, 23710, 23216, 22711, 22196, 21670, 21134, 20588, 20033, 19468, 18894, 18312, 17721, 17121, 16514, 15899, 15276, 14647, 14010, 13367, 12718, 12063, 11402, 10736, 10065, 9389, 8709, 8024, 7336, 6645, 5950, 5253, 4553, 3851, 3148, 2443, 1736, 1029, 322, -385, -1093, -1800, -2506, -3211, -3914, -4616, -5316, -6013, -6707, -7398, -8086, -8770, -9449, -10125, -10796, -11461, -12122, -12776, -13425, -14067, -14703, -15332, -15954, -16568, -17175, -17774, -18364, -18946, -19519, -20083, -20637, -21182, -21717, -22242, -22756, -23260, -23753, -24235, -24706, -25165, -25612, -26048, -26471, -26882, -27280, -27666, -28039, -28398, -28745, -29078, -29397, -29703, -29995, -30273, -30536, -30786, -31021, -31242, -31448, -31639, -31816, -31978, -32125, -32257, -32374, -32475, -32562, -32633, -32690, -32731, -32756, -32767, -32762, -32742, -32706, -32656, -32590, -32509, -32412, -32301, -32174, -32033, -31877, -31705, -31519, -31318, -31103, -30873, -30629, -30370, -30097, -29811, -29510, -29196, -28867, -28526, -28171, -27803, -27422, -27028, -26622, -26203, -25772, -25329, -24874, -24408, -23930, -23441, -22941, -22430, -21909, -21378, -20836, -20285, -19725, -19155, -18577, -17989, -17394, -16790, -16178, -15559, -14933, -14299, -13659, -13013, -12360, -11702, -11038, -10369, -9696, -9017, -8335, -7649, -6959, -6265, -5569, -4871, -4170, -3467, -2762, -2057, -1350, -642}}, - {85U, 281U, {0, 733, 1465, 2196, 2927, 3655, 4383, 5107, 5830, 6549, 7265, 7978, 8686, 9390, 10090, 10784, 11473, 12157, 12834, 13505, 14169, 14826, 15475, 16117, 16751, 17377, 17993, 18601, 19199, 19788, 20367, 20936, 21494, 22042, 22578, 23104, 23617, 24119, 24609, 25087, 25552, 26004, 26443, 26869, 27282, 27681, 28066, 28437, 28794, 29136, 29464, 29777, 30076, 30359, 30627, 30880, 31117, 31339, 31545, 31736, 31910, 32069, 32211, 32338, 32448, 32542, 32620, 32681, 32726, 32755, 32767, 32763, 32743, 32706, 32653, 32584, 32498, 32396, 32278, 32144, 31994, 31827, 31645, 31447, 31233, 31004, 30759, 30499, 30224, 29933, 29627, 29307, 28972, 28623, 28259, 27881, 27489, 27083, 26664, 26232, 25786, 25328, 24857, 24373, 23877, 23370, 22850, 22320, 21778, 21225, 20662, 20088, 19504, 18910, 18308, 17695, 17074, 16445, 15807, 15162, 14508, 13848, 13180, 12506, 11826, 11140, 10448, 9751, 9050, 8343, 7633, 6919, 6201, 5480, 4756, 4030, 3302, 2573, 1842, 1110, 377, -354, -1087, -1818, -2549, -3279, -4007, -4733, -5457, -6178, -6896, -7610, -8321, -9027, -9729, -10426, -11118, -11804, -12485, -13159, -13827, -14487, -15141, -15787, -16425, -17054, -17676, -18288, -18891, -19485, -20069, -20643, -21207, -21760, -22302, -22833, -23353, -23861, -24357, -24841, -25313, -25772, -26217, -26650, -27070, -27476, -27868, -28246, -28611, -28961, -29296, -29617, -29923, -30214, -30490, -30751, -30996, -31226, -31440, -31638, -31821, -31988, -32139, -32273, -32392, -32494, -32581, -32650, -32704, -32741, -32762, -32767, -32755, -32726, -32682, -32621, -32543, -32450, -32340, -32214, -32072, -31914, -31740, -31550, -31345, -31123, -30886, -30634, -30366, -30084, -29786, -29473, -29145, -28803, -28447, -28076, -27692, -27293, -26881, -26455, -26016, -25565, -25100, -24623, -24133, -23632, -23118, -22594, -22057, -21510, -20952, -20384, -19805, -19216, -18618, -18011, -17394, -16769, -16136, -15494, -14845, -14188, -13524, -12854, -12176, -11493, -10804, -10110, -9411, -8707, -7998, -7286, -6570, -5851, -5128, -4404, -3677, -2948, -2217, -1486, -754}}, - {88U, 271U, {0, 759, 1518, 2276, 3032, 3788, 4541, 5291, 6039, 6783, 7524, 8261, 8994, 9721, 10444, 11160, 11871, 12575, 13273, 13964, 14647, 15322, 15989, 16647, 17296, 17937, 18567, 19188, 19798, 20397, 20986, 21563, 22129, 22683, 23225, 23754, 24271, 24774, 25265, 25741, 26204, 26653, 27087, 27507, 27912, 28303, 28678, 29037, 29381, 29709, 30022, 30318, 30598, 30861, 31108, 31338, 31552, 31748, 31928, 32090, 32235, 32363, 32473, 32566, 32641, 32699, 32740, 32762, 32768, 32755, 32725, 32678, 32613, 32530, 32430, 32313, 32178, 32026, 31857, 31670, 31467, 31247, 31010, 30756, 30486, 30199, 29897, 29578, 29243, 28893, 28527, 28146, 27749, 27338, 26912, 26472, 26017, 25549, 25067, 24571, 24062, 23541, 23006, 22459, 21901, 21330, 20748, 20155, 19551, 18936, 18312, 17677, 17033, 16380, 15718, 15048, 14370, 13684, 12990, 12290, 11583, 10869, 10150, 9426, 8696, 7962, 7223, 6481, 5735, 4986, 4235, 3481, 2725, 1968, 1209, 450, -308, -1067, -1825, -2583, -3339, -4093, -4845, -5595, -6341, -7084, -7824, -8559, -9289, -10015, -10735, -11449, -12157, -12859, -13554, -14241, -14921, -15593, -16257, -16911, -17557, -18193, -18820, -19436, -20042, -20637, -21221, -21794, -22355, -22904, -23441, -23965, -24476, -24974, -25459, -25930, -26387, -26830, -27259, -27673, -28072, -28456, -28825, -29178, -29516, -29837, -30143, -30433, -30706, -30963, -31203, -31426, -31633, -31822, -31995, -32150, -32288, -32409, -32512, -32598, -32666, -32717, -32750, -32766, -32764, -32744, -32707, -32652, -32580, -32491, -32383, -32259, -32117, -31958, -31782, -31589, -31378, -31151, -30907, -30647, -30370, -30077, -29768, -29443, -29101, -28745, -28373, -27985, -27583, -27166, -26734, -26288, -25828, -25353, -24866, -24365, -23851, -23324, -22784, -22232, -21669, -21094, -20507, -19909, -19301, -18683, -18054, -17416, -16768, -16111, -15446, -14772, -14090, -13401, -12705, -12002, -11292, -10577, -9855, -9129, -8397, -7661, -6921, -6177, -5430, -4680, -3927, -3172, -2416, -1658, -900}}, - {91U, 262U, {0, 785, 1569, 2353, 3135, 3915, 4693, 5469, 6241, 7010, 7775, 8535, 9290, 10040, 10784, 11522, 12254, 12978, 13695, 14404, 15105, 15797, 16480, 17154, 17818, 18472, 19115, 19747, 20367, 20976, 21573, 22158, 22730, 23288, 23834, 24366, 24884, 25387, 25876, 26350, 26809, 27253, 27681, 28093, 28489, 28868, 29231, 29578, 29907, 30219, 30514, 30791, 31051, 31293, 31517, 31722, 31910, 32079, 32230, 32363, 32476, 32572, 32648, 32706, 32745, 32765, 32766, 32749, 32713, 32658, 32584, 32492, 32381, 32251, 32103, 31936, 31751, 31548, 31327, 31088, 30831, 30556, 30264, 29954, 29627, 29284, 28923, 28546, 28152, 27743, 27317, 26876, 26419, 25947, 25460, 24959, 24443, 23913, 23370, 22813, 22243, 21660, 21065, 20458, 19839, 19208, 18567, 17915, 17253, 16580, 15899, 15208, 14508, 13800, 13085, 12361, 11631, 10894, 10150, 9401, 8647, 7887, 7123, 6355, 5583, 4808, 4030, 3250, 2468, 1685, 901, 116, -668, -1453, -2236, -3019, -3799, -4578, -5354, -6126, -6896, -7661, -8422, -9178, -9929, -10674, -11413, -12145, -12871, -13589, -14299, -15001, -15695, -16379, -17054, -17720, -18375, -19019, -19653, -20275, -20886, -21485, -22071, -22645, -23206, -23753, -24287, -24807, -25313, -25804, -26280, -26741, -27187, -27618, -28032, -28430, -28812, -29178, -29527, -29859, -30173, -30471, -30751, -31013, -31257, -31484, -31692, -31883, -32055, -32208, -32343, -32460, -32558, -32637, -32697, -32739, -32762, -32766, -32752, -32718, -32666, -32595, -32505, -32397, -32270, -32125, -31961, -31779, -31578, -31360, -31123, -30869, -30597, -30307, -30000, -29676, -29334, -28976, -28602, -28210, -27803, -27380, -26941, -26486, -26016, -25532, -25033, -24519, -23991, -23450, -22895, -22327, -21746, -21153, -20547, -19930, -19301, -18661, -18011, -17350, -16679, -15999, -15309, -14611, -13904, -13190, -12467, -11738, -11002, -10259, -9511, -8757, -7998, -7235, -6467, -5696, -4922, -4144, -3364, -2583, -1800, -1015}}, - {94U, 253U, {0, 813, 1626, 2438, 3248, 4056, 4862, 5664, 6463, 7259, 8049, 8835, 9615, 10390, 11158, 11919, 12673, 13419, 14157, 14886, 15605, 16316, 17016, 17705, 18384, 19052, 19707, 20351, 20982, 21600, 22205, 22796, 23373, 23936, 24484, 25017, 25534, 26036, 26522, 26991, 27444, 27880, 28299, 28700, 29084, 29449, 29797, 30126, 30437, 30728, 31001, 31255, 31490, 31705, 31901, 32077, 32233, 32369, 32486, 32582, 32659, 32715, 32751, 32767, 32763, 32739, 32694, 32629, 32544, 32440, 32315, 32170, 32006, 31821, 31617, 31394, 31151, 30890, 30609, 30309, 29991, 29654, 29299, 28925, 28534, 28126, 27700, 27257, 26797, 26321, 25829, 25320, 24796, 24257, 23703, 23134, 22551, 21954, 21344, 20720, 20084, 19435, 18774, 18102, 17419, 16725, 16020, 15306, 14582, 13849, 13108, 12359, 11602, 10838, 10067, 9290, 8507, 7720, 6927, 6130, 5329, 4525, 3719, 2909, 2099, 1286, 474, -339, -1152, -1964, -2775, -3585, -4392, -5196, -5997, -6795, -7588, -8377, -9161, -9939, -10710, -11476, -12234, -12984, -13727, -14461, -15186, -15902, -16608, -17304, -17989, -18663, -19326, -19977, -20615, -21241, -21854, -22453, -23038, -23609, -24166, -24708, -25234, -25745, -26240, -26719, -27182, -27627, -28056, -28467, -28861, -29237, -29596, -29935, -30257, -30560, -30844, -31109, -31355, -31581, -31788, -31976, -32143, -32291, -32419, -32528, -32616, -32684, -32732, -32759, -32767, -32754, -32721, -32668, -32595, -32502, -32389, -32256, -32103, -31930, -31738, -31526, -31294, -31043, -30774, -30485, -30177, -29851, -29507, -29144, -28763, -28365, -27949, -27516, -27066, -26599, -26116, -25617, -25102, -24572, -24026, -23466, -22891, -22302, -21700, -21084, -20455, -19813, -19159, -18494, -17817, -17129, -16431, -15722, -15004, -14276, -13540, -12795, -12043, -11283, -10516, -9742, -8963, -8178, -7388, -6594, -5795, -4993, -4188, -3380, -2570, -1758, -946}}, - {97U, 246U, {0, 835, 1670, 2504, 3336, 4166, 4994, 5818, 6638, 7454, 8265, 9071, 9871, 10664, 11451, 12230, 13001, 13764, 14518, 15262, 15996, 16720, 17433, 18135, 18825, 19503, 20168, 20820, 21458, 22082, 22693, 23288, 23868, 24433, 24981, 25514, 26030, 26529, 27011, 27475, 27921, 28350, 28759, 29150, 29523, 29875, 30209, 30523, 30817, 31091, 31345, 31578, 31791, 31983, 32154, 32305, 32434, 32543, 32630, 32696, 32741, 32764, 32766, 32747, 32707, 32645, 32562, 32458, 32333, 32187, 32019, 31832, 31623, 31394, 31144, 30874, 30584, 30274, 29945, 29596, 29228, 28840, 28434, 28010, 27567, 27107, 26628, 26133, 25620, 25091, 24546, 23984, 23407, 22815, 22208, 21586, 20950, 20301, 19639, 18964, 18276, 17577, 16866, 16144, 15412, 14670, 13918, 13157, 12387, 11610, 10825, 10033, 9234, 8430, 7620, 6804, 5985, 5162, 4335, 3505, 2674, 1840, 1005, 170, -665, -1500, -2334, -3166, -3997, -4825, -5650, -6471, -7288, -8100, -8907, -9708, -10503, -11291, -12071, -12844, -13608, -14364, -15110, -15847, -16573, -17288, -17992, -18685, -19365, -20033, -20687, -21328, -21956, -22569, -23167, -23750, -24318, -24870, -25406, -25925, -26428, -26913, -27381, -27831, -28263, -28677, -29071, -29447, -29804, -30142, -30460, -30758, -31036, -31294, -31531, -31748, -31945, -32120, -32275, -32409, -32521, -32613, -32683, -32732, -32760, -32767, -32752, -32716, -32658, -32580, -32480, -32359, -32217, -32054, -31870, -31666, -31441, -31195, -30930, -30644, -30338, -30012, -29667, -29303, -28920, -28517, -28097, -27658, -27201, -26726, -26234, -25725, -25199, -24657, -24099, -23525, -22935, -22331, -21713, -21080, -20433, -19774, -19101, -18416, -17719, -17010, -16291, -15561, -14820, -14070, -13311, -12544, -11768, -10984, -10193, -9396, -8593, -7784, -6970, -6151, -5328, -4502, -3673, -2842, -2009, -1174}}, - {100U, 240U, {0, 858, 1715, 2571, 3425, 4277, 5126, 5971, 6813, 7650, 8481, 9307, 10126, 10938, 11743, 12540, 13328, 14107, 14876, 15636, 16384, 17121, 17847, 18560, 19261, 19948, 20622, 21281, 21926, 22556, 23170, 23769, 24351, 24917, 25466, 25997, 26510, 27005, 27482, 27939, 28378, 28797, 29197, 29576, 29935, 30274, 30592, 30888, 31164, 31419, 31651, 31863, 32052, 32219, 32365, 32488, 32588, 32667, 32723, 32757, 32768, 32757, 32723, 32667, 32588, 32488, 32365, 32219, 32052, 31863, 31651, 31419, 31164, 30888, 30592, 30274, 29935, 29576, 29197, 28797, 28378, 27939, 27482, 27005, 26510, 25997, 25466, 24917, 24351, 23769, 23170, 22556, 21926, 21281, 20622, 19948, 19261, 18560, 17847, 17121, 16384, 15636, 14876, 14107, 13328, 12540, 11743, 10938, 10126, 9307, 8481, 7650, 6813, 5971, 5126, 4277, 3425, 2571, 1715, 858, 0, -857, -1714, -2570, -3424, -4276, -5125, -5970, -6812, -7649, -8480, -9306, -10125, -10937, -11742, -12539, -13327, -14106, -14875, -15635, -16383, -17120, -17846, -18559, -19260, -19947, -20621, -21280, -21925, -22555, -23169, -23768, -24350, -24916, -25465, -25996, -26509, -27004, -27481, -27938, -28377, -28796, -29196, -29575, -29934, -30273, -30591, -30887, -31163, -31418, -31650, -31862, -32051, -32218, -32364, -32487, -32587, -32666, -32722, -32756, -32767, -32756, -32722, -32666, -32587, -32487, -32364, -32218, -32051, -31862, -31650, -31418, -31163, -30887, -30591, -30273, -29934, -29575, -29196, -28796, -28377, -27938, -27481, -27004, -26509, -25996, -25465, -24916, -24350, -23768, -23169, -22555, -21925, -21280, -20621, -19947, -19260, -18559, -17846, -17120, -16383, -15635, -14875, -14106, -13327, -12539, -11742, -10937, -10125, -9306, -8480, -7649, -6812, -5970, -5125, -4276, -3424, -2570, -1714, -857}}, - {103U, 232U, {0, 888, 1775, 2661, 3545, 4426, 5304, 6178, 7048, 7912, 8771, 9623, 10468, 11305, 12134, 12955, 13765, 14566, 15356, 16134, 16901, 17656, 18397, 19125, 19839, 20538, 21222, 21891, 22544, 23180, 23799, 24400, 24984, 25549, 26096, 26623, 27131, 27619, 28086, 28533, 28959, 29364, 29747, 30109, 30448, 30765, 31059, 31331, 31579, 31805, 32007, 32185, 32340, 32471, 32578, 32662, 32721, 32757, 32768, 32755, 32718, 32658, 32573, 32464, 32332, 32175, 31996, 31792, 31566, 31316, 31043, 30747, 30429, 30088, 29726, 29341, 28935, 28508, 28060, 27591, 27102, 26593, 26064, 25517, 24950, 24366, 23763, 23143, 22506, 21853, 21183, 20498, 19798, 19083, 18354, 17612, 16857, 16090, 15310, 14520, 13719, 12907, 12087, 11257, 10419, 9574, 8721, 7862, 6997, 6127, 5253, 4375, 3493, 2609, 1724, 836, -50, -938, -1825, -2711, -3595, -4476, -5354, -6228, -7097, -7961, -8819, -9671, -10516, -11353, -12181, -13001, -13811, -14611, -15400, -16178, -16944, -17698, -18439, -19166, -19879, -20577, -21261, -21928, -22580, -23215, -23833, -24433, -25016, -25580, -26126, -26652, -27159, -27645, -28112, -28557, -28982, -29386, -29768, -30128, -30466, -30782, -31075, -31345, -31592, -31816, -32017, -32194, -32347, -32477, -32583, -32665, -32723, -32757, -32767, -32753, -32715, -32652, -32566, -32456, -32322, -32165, -31983, -31779, -31551, -31299, -31025, -30728, -30409, -30067, -29703, -29317, -28910, -28481, -28032, -27562, -27072, -26562, -26032, -25483, -24916, -24330, -23727, -23106, -22468, -21813, -21143, -20457, -19756, -19040, -18311, -17568, -16812, -16044, -15264, -14473, -13671, -12859, -12038, -11208, -10369, -9523, -8670, -7811, -6946, -6076, -5201, -4323, -3441, -2557, -1671, -784}}, - {107U, 224U, {0, 920, 1838, 2756, 3671, 4583, 5492, 6396, 7295, 8189, 9076, 9956, 10828, 11692, 12546, 13391, 14225, 15047, 15858, 16657, 17442, 18213, 18971, 19713, 20440, 21150, 21844, 22521, 23180, 23821, 24443, 25046, 25629, 26192, 26734, 27255, 27755, 28233, 28688, 29121, 29531, 29918, 30282, 30621, 30936, 31227, 31494, 31735, 31952, 32143, 32309, 32450, 32565, 32654, 32718, 32756, 32768, 32754, 32715, 32650, 32559, 32442, 32300, 32132, 31939, 31721, 31478, 31211, 30918, 30601, 30261, 29896, 29508, 29096, 28662, 28205, 27726, 27225, 26702, 26159, 25595, 25010, 24406, 23783, 23141, 22481, 21803, 21108, 20397, 19669, 18926, 18168, 17395, 16609, 15810, 14999, 14175, 13340, 12495, 11640, 10776, 9904, 9023, 8136, 7242, 6342, 5438, 4529, 3616, 2701, 1783, 865, -54, -973, -1892, -2809, -3724, -4636, -5545, -6449, -7348, -8241, -9128, -10007, -10879, -11742, -12596, -13440, -14273, -15095, -15905, -16703, -17487, -18258, -19014, -19756, -20482, -21191, -21884, -22560, -23218, -23858, -24479, -25080, -25662, -26224, -26765, -27285, -27783, -28260, -28714, -29145, -29554, -29940, -30301, -30639, -30953, -31243, -31508, -31748, -31963, -32153, -32317, -32456, -32570, -32658, -32720, -32756, -32767, -32752, -32711, -32644, -32551, -32433, -32290, -32121, -31926, -31707, -31462, -31193, -30899, -30581, -30238, -29872, -29483, -29070, -28634, -28176, -27695, -27193, -26669, -26125, -25559, -24974, -24369, -23744, -23101, -22440, -21761, -21065, -20353, -19624, -18880, -18121, -17348, -16561, -15761, -14949, -14125, -13289, -12444, -11588, -10723, -9850, -8969, -8082, -7187, -6287, -5382, -4473, -3561, -2645, -1728, -809}}, - {110U, 216U, {0, 951, 1902, 2851, 3797, 4740, 5679, 6614, 7543, 8465, 9381, 10288, 11187, 12076, 12955, 13824, 14680, 15525, 16356, 17173, 17976, 18764, 19536, 20291, 21030, 21750, 22452, 23136, 23800, 24444, 25067, 25669, 26249, 26808, 27343, 27856, 28345, 28811, 29252, 29668, 30059, 30425, 30766, 31080, 31368, 31630, 31865, 32074, 32255, 32409, 32536, 32635, 32707, 32751, 32768, 32757, 32718, 32652, 32558, 32437, 32289, 32113, 31910, 31681, 31424, 31142, 30833, 30497, 30137, 29750, 29339, 28903, 28443, 27959, 27451, 26920, 26366, 25790, 25192, 24573, 23934, 23274, 22595, 21896, 21179, 20444, 19692, 18924, 18139, 17339, 16525, 15697, 14855, 14001, 13135, 12258, 11371, 10474, 9568, 8654, 7733, 6805, 5872, 4934, 3991, 3045, 2097, 1147, 196, -755, -1705, -2655, -3602, -4546, -5486, -6421, -7351, -8275, -9192, -10101, -11002, -11893, -12775, -13645, -14504, -15351, -16185, -17005, -17811, -18602, -19377, -20136, -20878, -21603, -22309, -22996, -23664, -24312, -24939, -25546, -26131, -26694, -27234, -27752, -28246, -28716, -29162, -29583, -29980, -30351, -30697, -31017, -31310, -31578, -31818, -32032, -32219, -32379, -32511, -32616, -32693, -32743, -32766, -32760, -32727, -32667, -32579, -32463, -32321, -32151, -31953, -31729, -31478, -31201, -30897, -30567, -30212, -29831, -29425, -28994, -28539, -28059, -27556, -27030, -26481, -25909, -25316, -24701, -24066, -23410, -22735, -22040, -21327, -20596, -19847, -19082, -18301, -17504, -16693, -15867, -15028, -14176, -13313, -12438, -11553, -10658, -9754, -8842, -7922, -6996, -6063, -5126, -4184, -3239, -2291, -1341}}, - {114U, 209U, {0, 985, 1968, 2950, 3930, 4906, 5877, 6843, 7803, 8756, 9701, 10637, 11563, 12480, 13384, 14277, 15157, 16023, 16875, 17711, 18532, 19335, 20122, 20890, 21639, 22369, 23078, 23767, 24434, 25079, 25701, 26301, 26876, 27427, 27954, 28455, 28930, 29380, 29802, 30198, 30567, 30908, 31221, 31506, 31762, 31990, 32189, 32359, 32499, 32610, 32692, 32744, 32767, 32760, 32723, 32657, 32562, 32437, 32282, 32099, 31886, 31645, 31375, 31077, 30751, 30397, 30015, 29607, 29172, 28710, 28222, 27709, 27171, 26608, 26022, 25411, 24778, 24123, 23445, 22747, 22028, 21289, 20531, 19754, 18960, 18148, 17320, 16476, 15617, 14745, 13859, 12960, 12050, 11129, 10198, 9257, 8309, 7352, 6389, 5421, 4447, 3470, 2489, 1506, 522, -462, -1447, -2430, -3411, -4388, -5362, -6331, -7294, -8251, -9200, -10141, -11073, -11995, -12906, -13805, -14692, -15565, -16425, -17269, -18098, -18911, -19706, -20484, -21244, -21984, -22704, -23404, -24082, -24739, -25374, -25985, -26573, -27137, -27677, -28192, -28681, -29144, -29581, -29991, -30374, -30730, -31058, -31358, -31629, -31872, -32086, -32271, -32427, -32554, -32652, -32719, -32758, -32766, -32745, -32695, -32615, -32506, -32367, -32199, -32002, -31776, -31521, -31238, -30926, -30587, -30220, -29826, -29404, -28957, -28483, -27983, -27458, -26908, -26334, -25737, -25115, -24472, -23806, -23118, -22410, -21682, -20934, -20167, -19381, -18579, -17759, -16924, -16073, -15208, -14329, -13437, -12532, -11617, -10691, -9755, -8811, -7859, -6899, -5933, -4962, -3987, -3008, -2026, -1042}}, - {118U, 202U, {0, 1019, 2037, 3053, 4066, 5075, 6079, 7078, 8069, 9053, 10028, 10993, 11948, 12891, 13821, 14739, 15642, 16529, 17401, 18256, 19094, 19912, 20712, 21492, 22251, 22988, 23703, 24395, 25063, 25708, 26327, 26921, 27489, 28030, 28545, 29031, 29490, 29920, 30321, 30692, 31035, 31347, 31628, 31879, 32100, 32289, 32447, 32573, 32669, 32732, 32764, 32764, 32733, 32669, 32575, 32448, 32291, 32102, 31882, 31631, 31350, 31038, 30696, 30325, 29924, 29494, 29036, 28550, 28036, 27495, 26927, 26333, 25714, 25070, 24402, 23710, 22995, 22258, 21499, 20720, 19921, 19102, 18265, 17410, 16538, 15651, 14748, 13831, 12900, 11957, 11003, 10038, 9063, 8079, 7088, 6090, 5085, 4076, 3063, 2047, 1029, 10, -1008, -2026, -3042, -4055, -5064, -6068, -7067, -8058, -9042, -10017, -10982, -11937, -12880, -13811, -14728, -15632, -16520, -17392, -18247, -19084, -19903, -20703, -21483, -22242, -22979, -23695, -24387, -25056, -25700, -26320, -26914, -27482, -28024, -28539, -29025, -29484, -29914, -30316, -30688, -31030, -31343, -31625, -31876, -32097, -32286, -32444, -32571, -32667, -32731, -32763, -32763, -32732, -32669, -32575, -32449, -32291, -32103, -31883, -31633, -31352, -31040, -30699, -30328, -29927, -29498, -29040, -28554, -28040, -27499, -26932, -26338, -25720, -25076, -24408, -23716, -23001, -22265, -21506, -20727, -19928, -19109, -18272, -17418, -16546, -15659, -14756, -13839, -12909, -11966, -11012, -10047, -9072, -8088, -7097, -6099, -5095, -4085, -3072, -2057, -1039}}, - {123U, 195U, {0, 1055, 2109, 3161, 4209, 5253, 6292, 7324, 8348, 9364, 10370, 11366, 12349, 13320, 14277, 15219, 16146, 17055, 17947, 18821, 19675, 20508, 21320, 22110, 22877, 23621, 24340, 25034, 25701, 26342, 26956, 27542, 28099, 28628, 29126, 29594, 30032, 30438, 30813, 31156, 31467, 31745, 31990, 32202, 32381, 32525, 32637, 32714, 32758, 32767, 32743, 32684, 32592, 32466, 32306, 32113, 31886, 31627, 31335, 31010, 30653, 30264, 29844, 29393, 28911, 28399, 27858, 27288, 26690, 26064, 25411, 24732, 24027, 23298, 22544, 21766, 20966, 20145, 19302, 18440, 17558, 16658, 15741, 14808, 13859, 12896, 11919, 10930, 9930, 8919, 7900, 6872, 5836, 4795, 3749, 2699, 1646, 592, -462, -1517, -2570, -3620, -4667, -5709, -6745, -7774, -8794, -9806, -10808, -11798, -12776, -13741, -14692, -15627, -16546, -17448, -18332, -19197, -20042, -20866, -21669, -22449, -23206, -23939, -24647, -25329, -25985, -26614, -27216, -27789, -28334, -28849, -29334, -29789, -30213, -30606, -30967, -31296, -31592, -31856, -32086, -32283, -32447, -32577, -32674, -32736, -32765, -32760, -32720, -32647, -32540, -32399, -32225, -32017, -31776, -31502, -31195, -30856, -30485, -30082, -29648, -29184, -28689, -28164, -27611, -27028, -26418, -25780, -25115, -24425, -23709, -22968, -22204, -21417, -20607, -19776, -18925, -18054, -17164, -16257, -15332, -14392, -13437, -12467, -11485, -10491, -9486, -8472, -7448, -6417, -5379, -4336, -3288, -2236, -1183}}, - {127U, 189U, {0, 1092, 2183, 3271, 4355, 5435, 6509, 7575, 8633, 9682, 10720, 11745, 12758, 13757, 14740, 15707, 16657, 17588, 18499, 19390, 20260, 21106, 21930, 22729, 23503, 24251, 24972, 25665, 26329, 26965, 27570, 28145, 28688, 29200, 29679, 30126, 30538, 30917, 31262, 31572, 31847, 32086, 32290, 32458, 32590, 32685, 32745, 32768, 32754, 32705, 32619, 32496, 32338, 32144, 31914, 31648, 31348, 31012, 30642, 30238, 29801, 29330, 28827, 28292, 27726, 27128, 26501, 25844, 25158, 24445, 23704, 22937, 22144, 21327, 20487, 19623, 18738, 17832, 16906, 15961, 14999, 14019, 13025, 12016, 10993, 9958, 8913, 7857, 6793, 5721, 4643, 3559, 2472, 1382, 290, -801, -1892, -2981, -4067, -5148, -6223, -7292, -8352, -9403, -10444, -11473, -12490, -13492, -14480, -15451, -16405, -17341, -18258, -19155, -20030, -20883, -21713, -22518, -23299, -24054, -24782, -25482, -26155, -26798, -27411, -27994, -28546, -29066, -29554, -30009, -30431, -30819, -31173, -31492, -31776, -32025, -32238, -32416, -32557, -32662, -32731, -32764, -32760, -32720, -32644, -32531, -32382, -32198, -31977, -31721, -31430, -31104, -30743, -30348, -29919, -29458, -28963, -28436, -27878, -27289, -26669, -26020, -25342, -24636, -23902, -23142, -22356, -21546, -20711, -19854, -18974, -18073, -17152, -16213, -15255, -14280, -13289, -12284, -11265, -10233, -9190, -8137, -7075, -6005, -4928, -3846, -2760, -1670, -579}}, - {131U, 182U, {0, 1130, 2260, 3386, 4508, 5625, 6736, 7838, 8931, 10013, 11084, 12141, 13184, 14211, 15221, 16213, 17186, 18138, 19068, 19976, 20861, 21720, 22554, 23360, 24139, 24889, 25610, 26300, 26958, 27585, 28179, 28739, 29265, 29756, 30212, 30631, 31015, 31361, 31670, 31941, 32175, 32370, 32526, 32644, 32723, 32763, 32764, 32725, 32648, 32533, 32378, 32185, 31953, 31684, 31376, 31032, 30650, 30232, 29778, 29289, 28764, 28206, 27613, 26988, 26331, 25643, 24924, 24175, 23397, 22592, 21760, 20902, 20019, 19112, 18182, 17231, 16259, 15268, 14259, 13232, 12190, 11134, 10064, 8982, 7890, 6788, 5678, 4561, 3439, 2313, 1184, 53, -1076, -2205, -3332, -4455, -5572, -6683, -7785, -8879, -9962, -11033, -12090, -13134, -14162, -15173, -16166, -17139, -18093, -19024, -19933, -20819, -21679, -22514, -23322, -24102, -24853, -25575, -26267, -26927, -27555, -28150, -28712, -29240, -29733, -30190, -30611, -30996, -31345, -31655, -31928, -32164, -32360, -32519, -32638, -32719, -32761, -32763, -32727, -32652, -32538, -32385, -32194, -31964, -31696, -31391, -31048, -30668, -30252, -29799, -29311, -28789, -28232, -27641, -27018, -26362, -25675, -24957, -24210, -23434, -22630, -21799, -20942, -20060, -19154, -18225, -17275, -16304, -15314, -14305, -13280, -12239, -11183, -10113, -9032, -7940, -6839, -5729, -4613, -3491, -2365, -1236}}, - {136U, 176U, {0, 1171, 2340, 3506, 4668, 5824, 6972, 8112, 9241, 10358, 11462, 12552, 13625, 14681, 15718, 16736, 17731, 18705, 19654, 20578, 21476, 22347, 23189, 24001, 24783, 25533, 26250, 26934, 27584, 28198, 28777, 29318, 29822, 30288, 30716, 31104, 31453, 31761, 32029, 32255, 32441, 32585, 32688, 32749, 32768, 32745, 32681, 32574, 32426, 32237, 32007, 31735, 31423, 31071, 30680, 30249, 29780, 29272, 28727, 28146, 27528, 26876, 26189, 25468, 24715, 23931, 23116, 22271, 21398, 20498, 19572, 18620, 17645, 16647, 15628, 14589, 13531, 12457, 11366, 10260, 9142, 8012, 6872, 5722, 4566, 3404, 2237, 1068, -102, -1273, -2442, -3608, -4769, -5924, -7072, -8210, -9339, -10455, -11558, -12646, -13718, -14772, -15808, -16823, -17817, -18788, -19735, -20657, -21553, -22421, -23260, -24070, -24849, -25596, -26311, -26992, -27638, -28250, -28825, -29363, -29864, -30327, -30751, -31135, -31480, -31785, -32049, -32272, -32454, -32595, -32694, -32751, -32767, -32740, -32672, -32562, -32410, -32218, -31983, -31709, -31393, -31038, -30643, -30208, -29735, -29225, -28677, -28092, -27471, -26816, -26126, -25402, -24647, -23859, -23042, -22195, -21319, -20417, -19488, -18534, -17557, -16557, -15536, -14496, -13437, -12360, -11268, -10162, -9042, -7911, -6770, -5620, -4463, -3300, -2134, -964}}, - {141U, 170U, {0, 1212, 2422, 3629, 4831, 6026, 7213, 8391, 9556, 10709, 11847, 12969, 14073, 15158, 16222, 17264, 18282, 19275, 20242, 21181, 22091, 22971, 23820, 24636, 25418, 26165, 26877, 27552, 28189, 28788, 29347, 29866, 30344, 30781, 31175, 31527, 31836, 32101, 32322, 32500, 32632, 32720, 32763, 32762, 32715, 32624, 32488, 32308, 32083, 31815, 31503, 31147, 30750, 30310, 29829, 29307, 28745, 28143, 27503, 26825, 26111, 25361, 24576, 23758, 22907, 22025, 21112, 20171, 19202, 18207, 17187, 16143, 15078, 13992, 12886, 11763, 10624, 9470, 8304, 7126, 5938, 4742, 3539, 2332, 1122, -89, -1301, -2511, -3718, -4919, -6114, -7300, -8477, -9641, -10793, -11930, -13051, -14153, -15237, -16299, -17339, -18356, -19347, -20312, -21249, -22157, -23034, -23881, -24694, -25474, -26219, -26927, -27600, -28234, -28830, -29386, -29902, -30377, -30811, -31202, -31551, -31856, -32118, -32336, -32510, -32639, -32724, -32764, -32759, -32709, -32614, -32475, -32291, -32064, -31792, -31477, -31118, -30718, -30275, -29790, -29265, -28700, -28096, -27453, -26773, -26056, -25303, -24516, -23695, -22841, -21957, -21042, -20099, -19128, -18131, -17109, -16064, -14997, -13909, -12802, -11678, -10538, -9383, -8215, -7037, -5848, -4652, -3449, -2241, -1031}}, - {146U, 164U, {0, 1254, 2506, 3754, 4997, 6233, 7459, 8675, 9878, 11066, 12238, 13392, 14527, 15640, 16730, 17796, 18836, 19848, 20832, 21784, 22705, 23592, 24445, 25262, 26042, 26784, 27487, 28150, 28771, 29350, 29886, 30378, 30826, 31229, 31586, 31896, 32160, 32377, 32546, 32668, 32742, 32768, 32746, 32676, 32558, 32392, 32179, 31919, 31612, 31259, 30860, 30415, 29927, 29394, 28818, 28200, 27541, 26842, 26103, 25326, 24511, 23661, 22777, 21858, 20908, 19927, 18917, 17880, 16816, 15727, 14616, 13483, 12330, 11159, 9972, 8771, 7556, 6330, 5096, 3853, 2605, 1353, 100, -1153, -2406, -3654, -4898, -6134, -7361, -8578, -9782, -10971, -12145, -13300, -14436, -15552, -16644, -17712, -18754, -19768, -20754, -21709, -22632, -23522, -24378, -25198, -25981, -26726, -27432, -28098, -28722, -29305, -29844, -30340, -30791, -31198, -31558, -31872, -32140, -32361, -32534, -32659, -32737, -32767, -32748, -32682, -32568, -32406, -32197, -31940, -31637, -31287, -30892, -30451, -29966, -29437, -28865, -28250, -27594, -26898, -26162, -25388, -24576, -23729, -22847, -21931, -20984, -20005, -18998, -17962, -16900, -15814, -14704, -13573, -12421, -11252, -10066, -8865, -7652, -6427, -5193, -3951, -2703, -1452}}, - {151U, 159U, {0, 1298, 2595, 3887, 5173, 6452, 7720, 8975, 10217, 11443, 12651, 13839, 15005, 16147, 17264, 18354, 19416, 20446, 21445, 22410, 23340, 24233, 25088, 25903, 26678, 27411, 28101, 28747, 29348, 29902, 30410, 30870, 31281, 31643, 31955, 32218, 32429, 32590, 32700, 32758, 32764, 32720, 32624, 32476, 32278, 32029, 31729, 31380, 30981, 30534, 30039, 29496, 28908, 28274, 27595, 26873, 26109, 25304, 24459, 23576, 22655, 21699, 20709, 19687, 18633, 17551, 16440, 15304, 14144, 12962, 11759, 10538, 9300, 8048, 6783, 5507, 4223, 2932, 1636, 338, -960, -2257, -3550, -4838, -6119, -7390, -8649, -9895, -11125, -12337, -13530, -14702, -15851, -16975, -18072, -19141, -20180, -21187, -22161, -23100, -24003, -24868, -25694, -26480, -27224, -27925, -28582, -29195, -29761, -30281, -30754, -31178, -31553, -31878, -32153, -32378, -32552, -32675, -32747, -32767, -32735, -32653, -32518, -32333, -32097, -31811, -31475, -31089, -30654, -30171, -29641, -29064, -28442, -27775, -27064, -26311, -25516, -24682, -23808, -22897, -21951, -20969, -19955, -18909, -17834, -16731, -15601, -14447, -13270, -12073, -10856, -9623, -8374, -7112, -5839, -4557, -3267, -1973, -675}}, - {156U, 153U, {0, 1344, 2686, 4023, 5353, 6674, 7984, 9281, 10562, 11825, 13069, 14290, 15488, 16659, 17802, 18915, 19997, 21045, 22057, 23032, 23969, 24865, 25719, 26531, 27297, 28017, 28691, 29316, 29892, 30417, 30891, 31314, 31683, 31999, 32262, 32470, 32624, 32722, 32766, 32754, 32687, 32566, 32389, 32158, 31873, 31534, 31142, 30698, 30202, 29656, 29059, 28413, 27720, 26980, 26195, 25365, 24493, 23580, 22627, 21636, 20608, 19546, 18451, 17325, 16170, 14987, 13779, 12548, 11296, 10025, 8738, 7435, 6120, 4794, 3461, 2122, 779, -564, -1908, -3248, -4582, -5909, -7226, -8531, -9821, -11095, -12350, -13584, -14796, -15982, -17142, -18273, -19373, -20441, -21474, -22471, -23430, -24350, -25229, -26065, -26857, -27605, -28305, -28959, -29563, -30118, -30622, -31074, -31475, -31822, -32115, -32355, -32540, -32671, -32746, -32767, -32732, -32642, -32497, -32298, -32044, -31736, -31375, -30961, -30495, -29978, -29410, -28792, -28127, -27414, -26654, -25850, -25003, -24113, -23183, -22214, -21207, -20165, -19088, -17980, -16841, -15674, -14481, -13263, -12023, -10763, -9485, -8190, -6882, -5563, -4234, -2897, -1556}}, - {159U, 150U, {0, 1370, 2739, 4102, 5458, 6804, 8139, 9459, 10763, 12048, 13312, 14553, 15768, 16956, 18114, 19240, 20332, 21389, 22409, 23389, 24328, 25225, 26078, 26885, 27645, 28356, 29018, 29630, 30189, 30695, 31148, 31547, 31890, 32177, 32408, 32582, 32699, 32759, 32762, 32707, 32596, 32427, 32201, 31919, 31581, 31188, 30740, 30239, 29684, 29078, 28421, 27714, 26958, 26156, 25307, 24414, 23479, 22502, 21487, 20433, 19344, 18221, 17066, 15881, 14668, 13430, 12168, 10885, 9583, 8264, 6930, 5585, 4229, 2867, 1499, 129, -1241, -2609, -3973, -5330, -6678, -8013, -9335, -10641, -11928, -13194, -14436, -15654, -16844, -18005, -19134, -20230, -21291, -22314, -23298, -24241, -25142, -25999, -26810, -27575, -28291, -28957, -29573, -30138, -30649, -31107, -31511, -31859, -32151, -32388, -32567, -32690, -32755, -32763, -32714, -32608, -32444, -32224, -31947, -31614, -31226, -30784, -30287, -29738, -29136, -28484, -27781, -27030, -26232, -25388, -24499, -23568, -22595, -21583, -20532, -19446, -18326, -17174, -15992, -14782, -13546, -12286, -11005, -9705, -8387, -7055, -5711, -4356, -2994, -1627}}, - {162U, 148U, {0, 1391, 2780, 4163, 5539, 6905, 8259, 9597, 10919, 12220, 13500, 14755, 15984, 17184, 18353, 19489, 20590, 21653, 22678, 23661, 24602, 25499, 26350, 27153, 27907, 28611, 29263, 29863, 30408, 30899, 31335, 31713, 32035, 32298, 32504, 32651, 32739, 32768, 32738, 32649, 32501, 32294, 32030, 31707, 31327, 30891, 30399, 29853, 29252, 28599, 27894, 27139, 26335, 25484, 24586, 23645, 22660, 21635, 20571, 19470, 18333, 17164, 15963, 14734, 13478, 12198, 10896, 9574, 8235, 6882, 5515, 4139, 2756, 1367, -23, -1414, -2803, -4186, -5562, -6928, -8281, -9619, -10940, -12242, -13521, -14776, -16004, -17204, -18372, -19507, -20607, -21670, -22694, -23677, -24617, -25513, -26363, -27165, -27919, -28622, -29273, -29872, -30416, -30906, -31341, -31718, -32039, -32302, -32506, -32652, -32739, -32767, -32736, -32646, -32497, -32289, -32024, -31700, -31319, -30882, -30390, -29842, -29241, -28586, -27881, -27125, -26320, -25468, -24570, -23627, -22642, -21616, -20551, -19449, -18312, -17142, -15941, -14712, -13455, -12175, -10872, -9550, -8211, -6857, -5491, -4114, -2731, -1342}}, - {165U, 145U, {0, 1419, 2836, 4247, 5651, 7043, 8423, 9787, 11132, 12457, 13758, 15033, 16280, 17496, 18680, 19829, 20940, 22012, 23043, 24030, 24973, 25868, 26715, 27512, 28257, 28949, 29587, 30169, 30695, 31163, 31572, 31923, 32213, 32443, 32612, 32720, 32766, 32751, 32674, 32536, 32337, 32078, 31758, 31378, 30940, 30443, 29890, 29280, 28615, 27897, 27126, 26304, 25433, 24514, 23549, 22540, 21489, 20397, 19267, 18101, 16901, 15669, 14408, 13120, 11807, 10472, 9117, 7745, 6359, 4961, 3553, 2139, 721, -698, -2116, -3531, -4939, -6337, -7724, -9096, -10451, -11786, -13099, -14388, -15650, -16882, -18082, -19249, -20380, -21472, -22524, -23534, -24499, -25419, -26290, -27113, -27884, -28604, -29269, -29880, -30434, -30932, -31371, -31751, -32072, -32333, -32533, -32672, -32749, -32765, -32720, -32613, -32445, -32216, -31927, -31577, -31169, -30701, -30177, -29595, -28958, -28267, -27523, -26726, -25880, -24985, -24044, -23057, -22027, -20955, -19845, -18697, -17513, -16297, -15051, -13776, -12475, -11151, -9806, -8443, -7063, -5671, -4268, -2856, -1440}}, - {167U, 143U, {0, 1440, 2877, 4309, 5732, 7144, 8542, 9924, 11287, 12628, 13944, 15234, 16494, 17722, 18916, 20074, 21192, 22270, 23305, 24294, 25237, 26131, 26975, 27766, 28504, 29187, 29813, 30382, 30892, 31342, 31732, 32061, 32327, 32531, 32673, 32751, 32766, 32717, 32606, 32431, 32194, 31895, 31534, 31112, 30630, 30088, 29489, 28833, 28121, 27354, 26535, 25665, 24745, 23777, 22763, 21705, 20606, 19466, 18289, 17077, 15831, 14555, 13251, 11921, 10569, 9196, 7805, 6399, 4980, 3552, 2117, 679, -761, -2199, -3634, -5061, -6479, -7884, -9274, -10646, -11998, -13326, -14629, -15903, -17147, -18357, -19532, -20669, -21767, -22822, -23833, -24798, -25715, -26583, -27399, -28162, -28871, -29524, -30120, -30658, -31137, -31555, -31913, -32208, -32442, -32613, -32721, -32766, -32747, -32665, -32520, -32313, -32042, -31710, -31317, -30863, -30350, -29777, -29148, -28462, -27721, -26926, -26080, -25183, -24238, -23245, -22208, -21128, -20007, -18847, -17651, -16421, -15159, -13868, -12550, -11208, -9844, -8461, -7062, -5649, -4225, -2793, -1356}}, - {171U, 140U, {0, 1469, 2935, 4395, 5847, 7286, 8711, 10119, 11506, 12870, 14208, 15517, 16795, 18040, 19248, 20418, 21546, 22631, 23671, 24663, 25605, 26496, 27334, 28117, 28843, 29511, 30120, 30668, 31155, 31579, 31939, 32235, 32467, 32633, 32733, 32768, 32737, 32640, 32477, 32249, 31956, 31599, 31179, 30695, 30150, 29544, 28879, 28156, 27376, 26542, 25653, 24714, 23724, 22687, 21604, 20478, 19310, 18104, 16862, 15585, 14277, 12940, 11578, 10192, 8786, 7361, 5923, 4472, 3012, 1546, 77, -1391, -2857, -4318, -5770, -7210, -8636, -10044, -11432, -12797, -14137, -15448, -16728, -17974, -19185, -20356, -21487, -22574, -23616, -24611, -25556, -26450, -27290, -28076, -28805, -29476, -30088, -30640, -31130, -31557, -31921, -32220, -32455, -32625, -32729, -32767, -32739, -32645, -32486, -32262, -31972, -31618, -31201, -30721, -30179, -29577, -28915, -28195, -27418, -26586, -25700, -24763, -23776, -22742, -21661, -20537, -19372, -18168, -16927, -15652, -14346, -13010, -11649, -10264, -8859, -7436, -5997, -4547, -3088, -1622}}, - {173U, 138U, {0, 1490, 2978, 4459, 5931, 7391, 8835, 10261, 11666, 13047, 14401, 15724, 17016, 18272, 19490, 20668, 21803, 22893, 23936, 24929, 25871, 26759, 27591, 28367, 29084, 29740, 30335, 30867, 31336, 31739, 32077, 32348, 32552, 32689, 32759, 32760, 32694, 32560, 32359, 32090, 31756, 31355, 30890, 30360, 29768, 29114, 28400, 27627, 26797, 25912, 24973, 23982, 22941, 21853, 20720, 19544, 18327, 17073, 15783, 14461, 13108, 11729, 10325, 8900, 7456, 5997, 4525, 3044, 1557, 67, -1423, -2910, -4392, -5864, -7324, -8770, -10197, -11603, -12984, -14339, -15665, -16958, -18215, -19435, -20615, -21752, -22845, -23889, -24885, -25829, -26719, -27554, -28332, -29052, -29711, -30309, -30844, -31315, -31721, -32062, -32336, -32544, -32684, -32756, -32761, -32697, -32566, -32368, -32103, -31771, -31373, -30911, -30384, -29795, -29144, -28433, -27662, -26835, -25952, -25015, -24026, -22988, -21902, -20771, -19597, -18382, -17129, -15841, -14520, -13168, -11790, -10387, -8963, -7520, -6062, -4591, -3110, -1623}}, - {177U, 135U, {0, 1520, 3038, 4548, 6049, 7537, 9008, 10461, 11890, 13294, 14670, 16013, 17323, 18595, 19827, 21016, 22160, 23256, 24302, 25296, 26235, 27118, 27942, 28706, 29408, 30047, 30622, 31130, 31571, 31944, 32249, 32484, 32648, 32743, 32767, 32721, 32604, 32416, 32159, 31833, 31438, 30975, 30446, 29851, 29192, 28470, 27686, 26843, 25942, 24985, 23975, 22912, 21801, 20642, 19439, 18194, 16910, 15590, 14235, 12851, 11438, 10001, 8542, 7065, 5573, 4069, 2556, 1037, -483, -2003, -3518, -5026, -6523, -8006, -9472, -10917, -12339, -13734, -15100, -16433, -17730, -18990, -20209, -21384, -22513, -23593, -24623, -25599, -26521, -27385, -28191, -28935, -29618, -30236, -30789, -31277, -31696, -32048, -32330, -32543, -32685, -32757, -32759, -32690, -32551, -32341, -32062, -31714, -31297, -30813, -30263, -29647, -28968, -28226, -27423, -26562, -25643, -24669, -23641, -22563, -21436, -20263, -19047, -17789, -16493, -15161, -13797, -12403, -10982, -9538, -8073, -6591, -5095, -3587, -2072}}, - {179U, 133U, {0, 1543, 3082, 4615, 6137, 7645, 9137, 10608, 12056, 13477, 14869, 16227, 17549, 18833, 20074, 21271, 22421, 23521, 24569, 25563, 26500, 27378, 28195, 28950, 29641, 30265, 30823, 31313, 31733, 32082, 32361, 32567, 32702, 32764, 32753, 32669, 32513, 32286, 31986, 31615, 31175, 30665, 30087, 29443, 28733, 27959, 27124, 26228, 25274, 24265, 23201, 22086, 20921, 19711, 18457, 17161, 15828, 14460, 13059, 11630, 10175, 8697, 7200, 5687, 4161, 2627, 1086, -456, -1998, -3536, -5066, -6584, -8088, -9574, -11039, -12479, -13892, -15274, -16622, -17933, -19204, -20433, -21616, -22751, -23836, -24869, -25846, -26765, -27625, -28424, -29160, -29832, -30437, -30974, -31443, -31842, -32171, -32428, -32614, -32727, -32767, -32735, -32630, -32452, -32203, -31883, -31491, -31030, -30500, -29902, -29238, -28509, -27717, -26864, -25951, -24980, -23954, -22875, -21745, -20567, -19343, -18076, -16769, -15425, -14047, -12638, -11200, -9738, -8254, -6752, -5235, -3706, -2170}}, - {183U, 131U, {0, 1574, 3144, 4706, 6258, 7795, 9315, 10813, 12286, 13730, 15143, 16521, 17861, 19160, 20414, 21622, 22779, 23884, 24934, 25926, 26858, 27729, 28535, 29276, 29949, 30553, 31086, 31548, 31937, 32252, 32493, 32659, 32750, 32765, 32704, 32568, 32356, 32071, 31711, 31278, 30772, 30196, 29550, 28836, 28055, 27210, 26302, 25333, 24305, 23222, 22085, 20897, 19661, 18379, 17055, 15692, 14293, 12860, 11398, 9909, 8398, 6867, 5321, 3762, 2194, 622, -951, -2523, -4089, -5645, -7189, -8716, -10223, -11706, -13162, -14588, -15980, -17335, -18651, -19923, -21149, -22327, -23453, -24525, -25540, -26496, -27391, -28223, -28990, -29690, -30322, -30883, -31373, -31791, -32136, -32406, -32602, -32722, -32767, -32736, -32630, -32448, -32191, -31861, -31456, -30979, -30431, -29812, -29125, -28370, -27550, -26667, -25722, -24717, -23656, -22539, -21371, -20154, -18890, -17582, -16234, -14849, -13429, -11978, -10499, -8997, -7473, -5933, -4378, -2814, -1243}}, - {186U, 129U, {0, 1597, 3190, 4775, 6349, 7908, 9448, 10966, 12457, 13919, 15348, 16741, 18094, 19403, 20667, 21881, 23044, 24152, 25202, 26193, 27121, 27985, 28782, 29511, 30170, 30757, 31272, 31711, 32076, 32364, 32576, 32710, 32766, 32745, 32645, 32468, 32214, 31884, 31477, 30996, 30442, 29815, 29117, 28350, 27515, 26615, 25652, 24628, 23546, 22407, 21216, 19974, 18684, 17350, 15975, 14562, 13114, 11636, 10129, 8599, 7048, 5480, 3899, 2309, 714, -882, -2478, -4067, -5646, -7212, -8761, -10290, -11793, -13269, -14713, -16122, -17493, -18823, -20107, -21344, -22530, -23663, -24739, -25757, -26713, -27606, -28433, -29193, -29884, -30503, -31050, -31523, -31921, -32244, -32490, -32658, -32750, -32763, -32698, -32556, -32336, -32040, -31667, -31219, -30697, -30103, -29436, -28700, -27895, -27024, -26089, -25092, -24036, -22922, -21754, -20534, -19265, -17951, -16594, -15197, -13764, -12299, -10804, -9284, -7742, -6181, -4606, -3020, -1426}}, - {189U, 126U, {0, 1628, 3253, 4869, 6473, 8062, 9630, 11175, 12692, 14177, 15628, 17040, 18410, 19734, 21010, 22233, 23402, 24513, 25563, 26551, 27472, 28326, 29110, 29821, 30459, 31022, 31508, 31916, 32246, 32496, 32665, 32754, 32761, 32688, 32534, 32300, 31986, 31592, 31121, 30573, 29949, 29251, 28481, 27641, 26732, 25757, 24719, 23619, 22461, 21248, 19982, 18667, 17305, 15901, 14458, 12978, 11467, 9927, 8363, 6778, 5177, 3563, 1939, 311, -1316, -2942, -4560, -6167, -7759, -9331, -10881, -12403, -13895, -15353, -16772, -18150, -19484, -20769, -22003, -23182, -24304, -25367, -26366, -27300, -28167, -28964, -29690, -30342, -30919, -31420, -31844, -32188, -32453, -32638, -32742, -32765, -32707, -32569, -32350, -32051, -31673, -31216, -30682, -30073, -29389, -28633, -27806, -26910, -25947, -24921, -23833, -22686, -21483, -20227, -18921, -17568, -16172, -14735, -13263, -11757, -10223, -8663, -7082, -5483, -3871, -2249}}, - {192U, 124U, {0, 1653, 3302, 4943, 6571, 8182, 9773, 11338, 12875, 14379, 15846, 17273, 18656, 19991, 21276, 22506, 23679, 24792, 25841, 26825, 27740, 28585, 29357, 30054, 30674, 31217, 31680, 32062, 32362, 32581, 32716, 32767, 32736, 32621, 32422, 32142, 31779, 31336, 30812, 30210, 29531, 28777, 27950, 27052, 26084, 25050, 23953, 22794, 21577, 20305, 18982, 17610, 16193, 14736, 13240, 11711, 10152, 8567, 6960, 5336, 3698, 2051, 398, -1255, -2905, -4548, -6180, -7795, -9391, -10963, -12507, -14019, -15496, -16933, -18326, -19674, -20971, -22214, -23401, -24529, -25594, -26593, -27525, -28387, -29177, -29892, -30531, -31092, -31575, -31976, -32297, -32535, -32690, -32762, -32750, -32655, -32477, -32216, -31873, -31449, -30944, -30361, -29701, -28965, -28155, -27273, -26322, -25304, -24221, -23077, -21874, -20615, -19304, -17943, -16537, -15089, -13602, -12081, -10529, -8950, -7348, -5727, -4092, -2447}}, - {196U, 122U, {0, 1686, 3367, 5040, 6699, 8340, 9959, 11552, 13114, 14642, 16131, 17577, 18976, 20326, 21621, 22859, 24037, 25151, 26198, 27176, 28082, 28913, 29668, 30345, 30941, 31455, 31886, 32232, 32493, 32668, 32756, 32758, 32673, 32501, 32243, 31900, 31473, 30962, 30369, 29695, 28943, 28114, 27211, 26236, 25191, 24080, 22904, 21669, 20375, 19028, 17630, 16186, 14699, 13173, 11612, 10020, 8401, 6761, 5102, 3430, 1749, 63, -1621, -3303, -4976, -6636, -8278, -9898, -11492, -13055, -14584, -16074, -17522, -18924, -20275, -21572, -22813, -23992, -25109, -26159, -27139, -28048, -28882, -29640, -30320, -30919, -31436, -31870, -32220, -32484, -32662, -32754, -32758, -32677, -32508, -32254, -31914, -31489, -30981, -30391, -29721, -28972, -28146, -27246, -26273, -25231, -24122, -22949, -21715, -20424, -19079, -17683, -16240, -14754, -13230, -11670, -10079, -8462, -6822, -5164, -3492, -1812}}, - {199U, 120U, {0, 1711, 3417, 5113, 6796, 8460, 10101, 11715, 13297, 14842, 16347, 17807, 19219, 20578, 21881, 23125, 24305, 25420, 26464, 27437, 28335, 29155, 29897, 30556, 31132, 31624, 32029, 32346, 32576, 32716, 32768, 32730, 32603, 32386, 32082, 31690, 31212, 30648, 30001, 29272, 28463, 27577, 26615, 25581, 24477, 23307, 22072, 20778, 19427, 18023, 16569, 15071, 13531, 11955, 10346, 8709, 7048, 5367, 3673, 1968, 257, -1453, -3160, -4858, -6543, -8210, -9855, -11473, -13060, -14611, -16122, -17590, -19009, -20376, -21688, -22941, -24131, -25255, -26311, -27295, -28204, -29036, -29789, -30461, -31050, -31554, -31972, -32303, -32546, -32700, -32765, -32740, -32626, -32424, -32132, -31753, -31288, -30737, -30103, -29386, -28589, -27714, -26764, -25740, -24647, -23486, -22261, -20975, -19632, -18236, -16790, -15298, -13764, -12193, -10589, -8955, -7298, -5620, -3927, -2223}}, - {203U, 118U, {0, 1745, 3485, 5215, 6930, 8626, 10297, 11939, 13547, 15117, 16643, 18123, 19551, 20923, 22237, 23487, 24670, 25784, 26824, 27788, 28673, 29477, 30198, 30832, 31379, 31837, 32205, 32481, 32666, 32757, 32756, 32661, 32474, 32195, 31824, 31363, 30813, 30176, 29453, 28646, 27758, 26792, 25749, 24633, 23448, 22196, 20880, 19506, 18076, 16595, 15067, 13496, 11887, 10244, 8572, 6876, 5160, 3429, 1689, -55, -1800, -3539, -5269, -6984, -8679, -10349, -11990, -13597, -15165, -16690, -18168, -19595, -20965, -22277, -23525, -24706, -25817, -26855, -27817, -28699, -29501, -30218, -30850, -31394, -31850, -32214, -32488, -32669, -32757, -32753, -32656, -32466, -32183, -31810, -31346, -30793, -30153, -29427, -28618, -27728, -26759, -25714, -24596, -23408, -22154, -20836, -19460, -18029, -16546, -15017, -13444, -11834, -10190, -8517, -6820, -5104, -3373, -1633}}, - {206U, 116U, {0, 1771, 3536, 5291, 7031, 8750, 10444, 12107, 13734, 15322, 16865, 18358, 19798, 21180, 22500, 23754, 24939, 26051, 27087, 28044, 28919, 29709, 30413, 31028, 31552, 31984, 32322, 32566, 32715, 32768, 32725, 32587, 32354, 32026, 31604, 31091, 30486, 29792, 29011, 28146, 27198, 26171, 25067, 23890, 22643, 21330, 19955, 18521, 17033, 15496, 13913, 12290, 10630, 8940, 7223, 5486, 3732, 1968, 197, -1573, -3339, -5095, -6837, -8559, -10255, -11922, -13554, -15146, -16694, -18193, -19639, -21028, -22355, -23617, -24810, -25930, -26975, -27941, -28825, -29625, -30338, -30963, -31497, -31939, -32288, -32542, -32702, -32766, -32734, -32606, -32383, -32066, -31655, -31151, -30557, -29873, -29101, -28245, -27306, -26288, -25192, -24023, -22784, -21478, -20110, -18683, -17201, -15668, -14090, -12471, -10816, -9129, -7415, -5679, -3927, -2164}}, - {210U, 114U, {0, 1807, 3608, 5398, 7172, 8923, 10648, 12341, 13995, 15608, 17172, 18685, 20141, 21535, 22864, 24123, 25309, 26418, 27447, 28392, 29251, 30021, 30699, 31284, 31774, 32167, 32463, 32659, 32757, 32754, 32652, 32451, 32151, 31753, 31259, 30669, 29986, 29212, 28350, 27400, 26368, 25255, 24066, 22803, 21471, 20074, 18615, 17100, 15533, 13919, 12262, 10568, 8842, 7089, 5314, 3523, 1722, -84, -1890, -3691, -5481, -7253, -9004, -10727, -12418, -14071, -15681, -17244, -18754, -20207, -21598, -22924, -24180, -25362, -26467, -27492, -28433, -29288, -30054, -30728, -31308, -31794, -32182, -32473, -32665, -32758, -32751, -32644, -32438, -32134, -31731, -31232, -30638, -29951, -29173, -28306, -27353, -26316, -25200, -24007, -22741, -21406, -20005, -18544, -17027, -15457, -13841, -12182, -10486, -8759, -7005, -5229, -3438, -1636}}, - {218U, 110U, {0, 1870, 3734, 5586, 7419, 9228, 11008, 12751, 14453, 16108, 17710, 19254, 20736, 22150, 23492, 24758, 25942, 27042, 28054, 28975, 29801, 30530, 31159, 31687, 32112, 32432, 32646, 32754, 32755, 32650, 32437, 32120, 31697, 31171, 30544, 29817, 28993, 28074, 27064, 25966, 24783, 23519, 22179, 20766, 19286, 17742, 16141, 14488, 12787, 11044, 9265, 7457, 5624, 3772, 1909, 39, -1830, -3695, -5547, -7381, -9190, -10970, -12715, -14417, -16073, -17676, -19222, -20705, -22121, -23464, -24731, -25918, -27020, -28033, -28956, -29784, -30515, -31146, -31677, -32103, -32425, -32642, -32752, -32755, -32652, -32442, -32126, -31706, -31182, -30557, -29832, -29010, -28093, -27085, -25988, -24807, -23545, -22206, -20795, -19316, -17774, -16174, -14521, -12821, -11079, -9302, -7493, -5661, -3810, -1946}}, - {225U, 106U, {0, 1935, 3863, 5778, 7673, 9541, 11375, 13170, 14919, 16616, 18255, 19830, 21336, 22767, 24119, 25387, 26566, 27653, 28643, 29533, 30319, 31000, 31573, 32036, 32387, 32625, 32748, 32758, 32653, 32435, 32103, 31659, 31104, 30441, 29672, 28799, 27826, 26755, 25591, 24338, 23000, 21582, 20088, 18524, 16895, 15208, 13467, 11680, 9851, 7989, 6098, 4186, 2260, 325, -1609, -3539, -5457, -7355, -9228, -11069, -12871, -14628, -16334, -17983, -19569, -21087, -22531, -23897, -25179, -26374, -27476, -28482, -29389, -30194, -30893, -31484, -31965, -32335, -32592, -32735, -32763, -32678, -32478, -32165, -31740, -31204, -30559, -29807, -28952, -27995, -26941, -25792, -24554, -23229, -21824, -20343, -18790, -17172, -15494, -13762, -11982, -10160, -8303, -6416, -4507, -2583}}, - {229U, 105U, {0, 1964, 3921, 5864, 7786, 9680, 11539, 13357, 15127, 16842, 18496, 20084, 21600, 23038, 24394, 25661, 26837, 27916, 28894, 29768, 30536, 31194, 31739, 32170, 32486, 32685, 32766, 32729, 32575, 32304, 31916, 31413, 30798, 30072, 29238, 28298, 27257, 26118, 24885, 23562, 22155, 20668, 19106, 17476, 15783, 14033, 12233, 10389, 8507, 6595, 4660, 2707, 745, -1219, -3180, -5129, -7060, -8965, -10838, -12673, -14461, -16198, -17876, -19490, -21034, -22502, -23889, -25191, -26402, -27517, -28534, -29449, -30257, -30956, -31545, -32019, -32379, -32622, -32748, -32756, -32646, -32419, -32075, -31616, -31043, -30359, -29565, -28665, -27662, -26560, -25362, -24072, -22696, -21239, -19705, -18100, -16431, -14702, -12920, -11091, -9223, -7322, -5394, -3447, -1488}}, - {233U, 103U, {0, 2003, 3998, 5978, 7936, 9864, 11756, 13603, 15400, 17139, 18814, 20418, 21946, 23393, 24751, 26017, 27186, 28254, 29215, 30068, 30807, 31432, 31939, 32327, 32594, 32739, 32762, 32662, 32440, 32097, 31634, 31052, 30354, 29543, 28622, 27593, 26461, 25231, 23906, 22491, 20993, 19416, 17766, 16050, 14274, 12445, 10569, 8653, 6705, 4733, 2742, 741, -1261, -3260, -5247, -7214, -9154, -11060, -12925, -14741, -16502, -18201, -19832, -21389, -22867, -24258, -25559, -26765, -27870, -28871, -29764, -30546, -31214, -31765, -32197, -32509, -32699, -32767, -32712, -32535, -32237, -31818, -31280, -30625, -29855, -28974, -27985, -26891, -25696, -24405, -23023, -21555, -20007, -18383, -16691, -14936, -13126, -11266, -9365, -7428, -5464, -3479, -1481}}, - {241U, 99U, {0, 2073, 4138, 6186, 8209, 10199, 12149, 14050, 15894, 17675, 19385, 21018, 22566, 24024, 25385, 26645, 27799, 28840, 29767, 30574, 31258, 31818, 32250, 32552, 32725, 32766, 32676, 32455, 32104, 31624, 31018, 30287, 29436, 28466, 27382, 26189, 24890, 23492, 22000, 20420, 18757, 17020, 15215, 13348, 11428, 9463, 7459, 5426, 3371, 1302, -771, -2842, -4901, -6941, -8953, -10929, -12861, -14742, -16564, -18319, -20001, -21603, -23118, -24541, -25866, -27086, -28199, -29198, -30080, -30842, -31480, -31992, -32376, -32631, -32754, -32747, -32608, -32338, -31939, -31412, -30759, -29983, -29087, -28075, -26949, -25716, -24380, -22946, -21421, -19809, -18118, -16355, -14526, -12639, -10701, -8720, -6704, -4662, -2601}}, - {250U, 96U, {0, 2146, 4282, 6400, 8491, 10545, 12554, 14509, 16402, 18224, 19968, 21627, 23192, 24658, 26019, 27267, 28398, 29408, 30291, 31045, 31665, 32149, 32495, 32702, 32768, 32694, 32479, 32125, 31633, 31005, 30244, 29353, 28337, 27198, 25943, 24577, 23105, 21534, 19870, 18121, 16295, 14398, 12440, 10428, 8372, 6279, 4160, 2022, -123, -2268, -4404, -6520, -8609, -10661, -12667, -14619, -16508, -18326, -20065, -21718, -23278, -24739, -26092, -27334, -28459, -29461, -30337, -31083, -31695, -32172, -32510, -32708, -32766, -32684, -32461, -32099, -31599, -30964, -30195, -29297, -28273, -27128, -25867, -24494, -23016, -21439, -19771, -18017, -16186, -14286, -12324, -10310, -8251, -6157, -4036, -1898}}, - {254U, 94U, {0, 2178, 4347, 6496, 8617, 10699, 12734, 14713, 16627, 18467, 20226, 21895, 23467, 24935, 26293, 27535, 28655, 29648, 30510, 31237, 31826, 32274, 32580, 32741, 32757, 32629, 32356, 31940, 31382, 30686, 29854, 28890, 27799, 26584, 25251, 23807, 22258, 20610, 18871, 17049, 15151, 13186, 11163, 9090, 6977, 4834, 2668, 492, -1687, -3858, -6013, -8141, -10233, -12279, -14271, -16201, -18058, -19836, -21526, -23120, -24613, -25996, -27265, -28412, -29435, -30327, -31084, -31705, -32185, -32522, -32716, -32765, -32669, -32429, -32045, -31519, -30854, -30053, -29118, -28055, -26867, -25561, -24141, -22615, -20989, -19270, -17466, -15584, -13633, -11623, -9560, -7456, -5318, -3157}} +const struct CTCSS_TABLE { + uint8_t frequency; + uint16_t length; + q15_t increment; +} CTCSS_TABLE[] = { + { 67U, 358U, 92}, + { 69U, 346U, 95}, + { 71U, 334U, 99}, + { 74U, 323U, 102}, + { 77U, 312U, 106}, + { 79U, 301U, 109}, + { 82U, 291U, 113}, + { 85U, 281U, 117}, + { 88U, 271U, 121}, + { 91U, 262U, 125}, + { 94U, 253U, 130}, + { 97U, 246U, 133}, + {100U, 240U, 137}, + {103U, 232U, 142}, + {107U, 224U, 147}, + {110U, 216U, 152}, + {114U, 209U, 157}, + {118U, 202U, 163}, + {123U, 195U, 168}, + {127U, 189U, 174}, + {131U, 182U, 180}, + {136U, 176U, 187}, + {141U, 170U, 193}, + {146U, 164U, 200}, + {151U, 159U, 207}, + {156U, 153U, 214}, + {159U, 150U, 219}, + {162U, 148U, 222}, + {165U, 145U, 226}, + {167U, 143U, 230}, + {171U, 140U, 234}, + {173U, 138U, 238}, + {177U, 135U, 243}, + {179U, 133U, 246}, + {183U, 131U, 251}, + {186U, 129U, 255}, + {189U, 126U, 260}, + {192U, 124U, 264}, + {196U, 122U, 269}, + {199U, 120U, 273}, + {203U, 118U, 278}, + {206U, 116U, 282}, + {210U, 114U, 288}, + {218U, 110U, 298}, + {225U, 106U, 309}, + {229U, 105U, 313}, + {233U, 103U, 319}, + {241U, 99U, 331}, + {250U, 96U, 342}, + {254U, 94U, 347} }; + const uint8_t CTCSS_TABLE_LEN = 50U; CFMCTCSSTX::CFMCTCSSTX() : -m_entry(NULL), -m_level(128 * 128), +m_values(NULL), +m_length(0U), m_n(0U) { } uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) { + struct CTCSS_TABLE* entry = NULL; + for (uint8_t i = 0U; i < CTCSS_TABLE_LEN; i++) { - if (CTCSS_TONES[i].m_frequency == frequency) { - m_entry = CTCSS_TONES + i; + if (CTCSS_TABLE[i].frequency == frequency) { + entry = CTCSS_TABLE + i; break; } } - m_level = q15_t(level * 128); + if (entry == NULL) + return 4U; - return m_entry == NULL ? 4U : 0U; + m_length = entry->length; + + m_values = new q15_t[m_length]; + + q15_t arg = 0; + for (uint16_t i = 0U; i < m_length; i++) { + q31_t value = ::arm_sin_q15(arg) * q15_t(level * 128); + m_values[i] = q15_t(__SSAT((value >> 15), 16)); + + arg += entry->increment; + } + + return 0U; } void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) { - if (m_entry == NULL) - return; - for (uint8_t i = 0U; i < length; i++) { - q31_t sample = m_entry->m_values[m_n] * m_level; - samples[i] = q15_t(__SSAT((sample >> 15), 16)); + samples[i] = m_values[m_n]; m_n++; - if (m_n >= m_entry->m_length) + if (m_n >= m_length) m_n = 0U; } } diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index 5eee1c4..61b404e 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -21,12 +21,6 @@ #include "Config.h" -struct CTCSS_TABLE { - uint8_t m_frequency; - uint16_t m_length; - q15_t m_values[360U]; -}; - class CFMCTCSSTX { public: CFMCTCSSTX(); @@ -36,9 +30,9 @@ public: void getAudio(q15_t* samples, uint8_t length); private: - CTCSS_TABLE* m_entry; - q15_t m_level; - uint16_t m_n; + q15_t* m_values; + uint16_t m_length; + uint16_t m_n; }; #endif From 95e76f387c4c980ca85960cafb63010f9a504926 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 16 Apr 2020 21:15:15 +0100 Subject: [PATCH 017/119] Complete the CW keyer. --- FMCTCSSTX.cpp | 12 +++++----- FMKeyer.cpp | 61 ++++++++++++++++++++++++++++++++++++++++++--------- FMKeyer.h | 8 +++++-- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 0513f13..4c92577 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -24,7 +24,7 @@ const struct CTCSS_TABLE { uint8_t frequency; uint16_t length; q15_t increment; -} CTCSS_TABLE[] = { +} CTCSS_TABLE_DATA[] = { { 67U, 358U, 92}, { 69U, 346U, 95}, { 71U, 334U, 99}, @@ -77,7 +77,7 @@ const struct CTCSS_TABLE { {254U, 94U, 347} }; -const uint8_t CTCSS_TABLE_LEN = 50U; +const uint8_t CTCSS_TABLE_DATA_LEN = 50U; CFMCTCSSTX::CFMCTCSSTX() : m_values(NULL), @@ -88,11 +88,11 @@ m_n(0U) uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) { - struct CTCSS_TABLE* entry = NULL; + const CTCSS_TABLE* entry = NULL; - for (uint8_t i = 0U; i < CTCSS_TABLE_LEN; i++) { - if (CTCSS_TABLE[i].frequency == frequency) { - entry = CTCSS_TABLE + i; + for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { + if (CTCSS_TABLE_DATA[i].frequency == frequency) { + entry = CTCSS_TABLE_DATA + i; break; } } diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 56aa3d3..925a484 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -76,18 +76,20 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02 #define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) CFMKeyer::CFMKeyer() : -m_level(128 * 128), m_wanted(false), -m_running(false), m_poBuffer(), -m_poLen(0U) +m_poLen(0U), +m_poPos(0U), +m_dotLen(0U), +m_dotPos(0U), +m_audio(NULL), +m_audioLen(0U), +m_audioPos(0U) { } uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) { - m_level = q15_t(level * 128); - for (uint8_t i = 0U; text[i] != '\0'; i++) { for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { if (SYMBOL_LIST[j].c == text[i]) { @@ -107,27 +109,66 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, } } + q15_t value = q15_t(level * 128); + + m_dotLen = 24000U / speed; // In samples + + m_audioLen = 24000U / frequency; // In samples + + m_audio = new q15_t[m_audioLen]; + + for (uint16_t i = 0U; i < m_audioLen; i++) { + if (i < (m_audioLen / 2U)) + m_audio[i] = value; + else + m_audio[i] = -value; + } + return 0U; } void CFMKeyer::getAudio(q15_t* samples, uint8_t length) { - if (!m_wanted && !m_running) + if (!m_wanted) return; + + for (uint8_t i = 0U; i < length; i++) { + bool b = READ_BIT(m_poBuffer, m_poPos); + if (b) + samples[i] += m_audio[m_audioPos]; + + m_audioPos++; + if (m_audioPos >= m_audioLen) + m_audioPos = 0U; + m_dotPos++; + if (m_dotPos >= m_dotLen) { + m_dotPos = 0U; + m_poPos++; + if (m_poPos >= m_poLen) { + stop(); + return; + } + } + } } void CFMKeyer::start() { - m_wanted = true; + m_wanted = true; + m_poPos = 0U; + m_dotPos = 0U; + m_audioPos = 0U; } void CFMKeyer::stop() { - m_wanted = false; - m_running = false; + m_wanted = false; + m_poPos = 0U; + m_dotPos = 0U; + m_audioPos = 0U; } bool CFMKeyer::isRunning() const { - return m_running; + return m_poPos > 0U || m_dotPos > 0U || m_audioPos > 0U; } diff --git a/FMKeyer.h b/FMKeyer.h index dbcb011..3676522 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -35,11 +35,15 @@ public: bool isRunning() const; private: - q15_t m_level; bool m_wanted; - bool m_running; uint8_t m_poBuffer[1000U]; uint16_t m_poLen; + uint16_t m_poPos; + uint16_t m_dotLen; + uint16_t m_dotPos; + q15_t* m_audio; + uint16_t m_audioLen; + uint16_t m_audioPos; }; #endif From bc889f3d264a648138f189112ad01a6425325e0d Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 17 Apr 2020 13:11:21 +0100 Subject: [PATCH 018/119] Handle some extra edge cases. --- FM.cpp | 7 +++++-- FMKeyer.cpp | 3 +++ FMTimeout.cpp | 2 +- SerialPort.cpp | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/FM.cpp b/FM.cpp index c455858..3e600eb 100644 --- a/FM.cpp +++ b/FM.cpp @@ -154,6 +154,11 @@ void CFM::stateMachine(bool validSignal, uint8_t length) default: break; } + + if (m_state == FS_LISTENING && m_modemState == STATE_FM) { + if (!m_callsign.isRunning() && !m_rfAck.isRunning()) + m_modemState = STATE_IDLE; + } } void CFM::listeningState(bool validSignal) @@ -188,7 +193,6 @@ void CFM::kerchunkState(bool validSignal) m_ackMinTimer.stop(); m_callsignTimer.stop(); m_holdoffTimer.stop(); - m_modemState = STATE_IDLE; } } @@ -259,7 +263,6 @@ void CFM::hangState(bool validSignal) m_callsignTimer.stop(); m_holdoffTimer.stop(); - m_modemState = STATE_IDLE; } } diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 925a484..77da003 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -154,6 +154,9 @@ void CFMKeyer::getAudio(q15_t* samples, uint8_t length) void CFMKeyer::start() { + if (isRunning()) + return; + m_wanted = true; m_poPos = 0U; m_dotPos = 0U; diff --git a/FMTimeout.cpp b/FMTimeout.cpp index 0090f2e..38030f4 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -21,7 +21,7 @@ #include "FMTimeout.h" // 400 Hz sine wave at 24000 Hz sample rate -const q15_t BUSY_AUDIO[] = {0, 3426, 6813, 10126, 13328, 16384, 19261, 21926, 24351, 26510, 28378, 29935, 31164, 32052, 32588, 32768, 32588, 32052, 31164, 29935, 28378, 26510, 24351, +const q15_t BUSY_AUDIO[] = {0, 3426, 6813, 10126, 13328, 16384, 19261, 21926, 24351, 26510, 28378, 29935, 31164, 32052, 32588, 32767, 32588, 32052, 31164, 29935, 28378, 26510, 24351, 21926, 19261, 16384, 13328, 10126, 6813, 3425, 0, -3425, -6813, -10126, -13328, -16384, -19261, -21926, -24351, -26510, -28378, -29935, -31164, -32052, -32588, -32768, -32588, -32052, -31164, -29935, -28378, -26510, -24351, -21926, -19261, -16384, -13328, -10126, -6813, -3425}; const uint8_t BUSY_AUDIO_LEN = 60U; diff --git a/SerialPort.cpp b/SerialPort.cpp index 7256571..63313e8 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200415 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200417 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 4a85122483a9c610d32a84417eff8ac05fa2f07f Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 17 Apr 2020 14:11:42 +0100 Subject: [PATCH 019/119] CTCSS tones are added to the audio, not replacing it. --- FMCTCSSTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 4c92577..c2fb2c7 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -118,7 +118,7 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) { for (uint8_t i = 0U; i < length; i++) { - samples[i] = m_values[m_n]; + samples[i] += m_values[m_n]; m_n++; if (m_n >= m_length) From 564260b2f1922577e5b949832e68aeac47b2c06b Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 17 Apr 2020 18:45:42 +0100 Subject: [PATCH 020/119] Add the main audio filter. --- FM.cpp | 37 ++++++++++++++++++++++++++++++------- FM.h | 32 +++++++++++++++++--------------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/FM.cpp b/FM.cpp index 3e600eb..8f419db 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,7 +20,28 @@ #include "Globals.h" #include "FM.h" +q15_t FILTER_COEFFS[] = { + -4, 0, 1, -2, -9, -15, -16, -11, -2, 4, 6, 1, -7, -15, + -17, -9, 3, 16, 21, 14, 0, -12, -14, -1, 21, 43, 51, 41, + 19, -1, -5, 14, 50, 85, 98, 81, 44, 9, 0, 28, 80, 130, + 147, 120, 60, 2, -17, 16, 88, 157, 178, 132, 39, -52, -90, -49, + 46, 140, 166, 96, -42, -182, -245, -195, -63, 67, 106, 7, -194, -399, + -496, -429, -238, -41, 26, -106, -396, -697, -843, -743, -444, -121, 12, -165, + -603, -1084, -1329, -1163, -629, -6, 320, 67, -759, -1803, -2474, -2204, -739, 1695, + 4421, 6556, 7363, 6556, 4421, 1695, -739, -2204, -2474, -1803, -759, 67, 320, -6, + -629, -1163, -1329, -1084, -603, -165, 12, -121, -444, -743, -843, -697, -396, -106, + 26, -41, -238, -429, -496, -399, -194, 7, 106, 67, -63, -195, -245, -182, + -42, 96, 166, 140, 46, -49, -90, -52, 39, 132, 178, 157, 88, 16, + -17, 2, 60, 120, 147, 130, 80, 28, 0, 9, 44, 81, 98, 85, + 50, 14, -5, -1, 19, 41, 51, 43, 21, -1, -14, -12, 0, 14, + 21, 16, 3, -9, -17, -15, -7, 1, 6, 4, -2, -11, -16, -15, + -9, -2, 1, 0, -4}; + +const uint16_t FILTER_COEFFS_LEN = 201U; + CFM::CFM() : +m_filter(), +m_filterState(), m_callsign(), m_rfAck(), m_goertzel(), @@ -37,12 +58,15 @@ m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer() { + ::memset(m_filterState, 0x00U, 230U * sizeof(q15_t)); + + m_filter.numTaps = FILTER_COEFFS_LEN; + m_filter.pState = m_filterState; + m_filter.pCoeffs = FILTER_COEFFS; } void CFM::samples(q15_t* samples, uint8_t length) { - // De-emphasis - bool validSignal = m_goertzel.process(samples, length); stateMachine(validSignal, length); @@ -65,13 +89,12 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) m_timeoutTone.getAudio(samples, length); - // Band-pass filter + q15_t output[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_filter, samples, output, length); - m_ctcss.getAudio(samples, length); + m_ctcss.getAudio(output, length); - // Pre-emphasis - - io.write(STATE_FM, samples, length); + io.write(STATE_FM, output, length); } void CFM::process() diff --git a/FM.h b/FM.h index ef5aac5..bcea9ab 100644 --- a/FM.h +++ b/FM.h @@ -52,21 +52,23 @@ public: uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: - CFMKeyer m_callsign; - CFMKeyer m_rfAck; - CFMGoertzel m_goertzel; - CFMCTCSSTX m_ctcss; - CFMTimeout m_timeoutTone; - FM_STATE m_state; - bool m_callsignAtStart; - bool m_callsignAtEnd; - CFMTimer m_callsignTimer; - CFMTimer m_timeoutTimer; - CFMTimer m_holdoffTimer; - CFMTimer m_kerchunkTimer; - CFMTimer m_ackMinTimer; - CFMTimer m_ackDelayTimer; - CFMTimer m_hangTimer; + arm_fir_instance_q15 m_filter; + q15_t m_filterState[230U]; // NoTaps + BlockSize - 1, 201 + 20 - 1 plus some spare + CFMKeyer m_callsign; + CFMKeyer m_rfAck; + CFMGoertzel m_goertzel; + CFMCTCSSTX m_ctcss; + CFMTimeout m_timeoutTone; + FM_STATE m_state; + bool m_callsignAtStart; + bool m_callsignAtEnd; + CFMTimer m_callsignTimer; + CFMTimer m_timeoutTimer; + CFMTimer m_holdoffTimer; + CFMTimer m_kerchunkTimer; + CFMTimer m_ackMinTimer; + CFMTimer m_ackDelayTimer; + CFMTimer m_hangTimer; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From d1c106b3b8d08f55de27817be541e57feef68045 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 17 Apr 2020 18:53:28 +0100 Subject: [PATCH 021/119] Add state machine debugging messages. --- FM.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 8f419db..af70fb1 100644 --- a/FM.cpp +++ b/FM.cpp @@ -179,17 +179,21 @@ void CFM::stateMachine(bool validSignal, uint8_t length) } if (m_state == FS_LISTENING && m_modemState == STATE_FM) { - if (!m_callsign.isRunning() && !m_rfAck.isRunning()) + if (!m_callsign.isRunning() && !m_rfAck.isRunning()) { + DEBUG1("Change to STATE_IDLE"); m_modemState = STATE_IDLE; + } } } void CFM::listeningState(bool validSignal) { if (m_kerchunkTimer.getTimeout() > 0U) { + DEBUG1("State to KERCHUNK"); m_state = FS_KERCHUNK; m_kerchunkTimer.start(); } else { + DEBUG1("State to RELAYING"); m_state = FS_RELAYING; if (m_callsignAtStart) sendCallsign(); @@ -199,6 +203,7 @@ void CFM::listeningState(bool validSignal) m_callsignTimer.start(); + DEBUG1("Change to STATE_FM"); m_modemState = STATE_FM; } @@ -206,10 +211,12 @@ void CFM::kerchunkState(bool validSignal) { if (validSignal) { if (m_kerchunkTimer.hasExpired()) { + DEBUG1("State to RELAYING"); m_state = FS_RELAYING; m_kerchunkTimer.stop(); } } else { + DEBUG1("State to LISTENING"); m_state = FS_LISTENING; m_kerchunkTimer.stop(); m_timeoutTimer.stop(); @@ -223,12 +230,14 @@ void CFM::relayingState(bool validSignal) { if (validSignal) { if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { + DEBUG1("State to TIMEOUT"); m_state = FS_TIMEOUT; m_ackMinTimer.stop(); m_timeoutTimer.stop(); m_timeoutTone.start(); } } else { + DEBUG1("State to RELAYING_WAIT"); m_state = FS_RELAYING_WAIT; m_ackDelayTimer.start(); } @@ -242,18 +251,22 @@ void CFM::relayingState(bool validSignal) void CFM::relayingWaitState(bool validSignal) { if (validSignal) { + DEBUG1("State to RELAYING"); m_state = FS_RELAYING; m_ackDelayTimer.stop(); } else { if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) { + DEBUG1("State to HANG"); m_state = FS_HANG; if (m_ackMinTimer.isRunning()) { if (m_ackMinTimer.hasExpired()) { + DEBUG1("Send ack"); m_rfAck.start(); m_ackMinTimer.stop(); } } else { + DEBUG1("Send ack"); m_rfAck.start(); m_ackMinTimer.stop(); } @@ -273,11 +286,14 @@ void CFM::relayingWaitState(bool validSignal) void CFM::hangState(bool validSignal) { if (validSignal) { + DEBUG1("State to RELAYING"); m_state = FS_RELAYING; + DEBUG1("Stop ack"); m_rfAck.stop(); beginRelaying(); } else { if (m_hangTimer.isRunning() && m_hangTimer.hasExpired()) { + DEBUG1("State to LISTENING"); m_state = FS_LISTENING; m_hangTimer.stop(); @@ -298,6 +314,7 @@ void CFM::hangState(bool validSignal) void CFM::timeoutState(bool validSignal) { if (!validSignal) { + DEBUG1("State to TIMEOUT_WAIT"); m_state = FS_TIMEOUT_WAIT; m_ackDelayTimer.start(); } @@ -311,12 +328,15 @@ void CFM::timeoutState(bool validSignal) void CFM::timeoutWaitState(bool validSignal) { if (validSignal) { + DEBUG1("State to TIMEOUT"); m_state = FS_TIMEOUT; m_ackDelayTimer.stop(); } else { if (m_ackDelayTimer.isRunning() && m_ackDelayTimer.hasExpired()) { + DEBUG1("State to HANG"); m_state = FS_HANG; m_timeoutTone.stop(); + DEBUG1("Send ack"); m_rfAck.start(); m_ackDelayTimer.stop(); m_ackMinTimer.stop(); @@ -335,10 +355,12 @@ void CFM::sendCallsign() { if (m_holdoffTimer.isRunning()) { if (m_holdoffTimer.hasExpired()) { + DEBUG1("Send callsign"); m_callsign.start(); m_holdoffTimer.start(); } } else { + DEBUG1("Send callsign"); m_callsign.start(); } } From 8ca756ec223804751c896e47ba7066e6eb096710 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 13:47:41 +0100 Subject: [PATCH 022/119] Simplify the FM arguments. --- FM.cpp | 4 ++-- FM.h | 2 +- SerialPort.cpp | 15 +++++++-------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/FM.cpp b/FM.cpp index af70fb1..4ff0a6a 100644 --- a/FM.cpp +++ b/FM.cpp @@ -105,7 +105,7 @@ void CFM::reset() { } -uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd) +uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t level, bool callsignAtStart, bool callsignAtEnd) { m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; @@ -118,7 +118,7 @@ uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency m_holdoffTimer.setTimeout(holdoffTime, 0U); m_callsignTimer.setTimeout(callsignTime, 0U); - return m_callsign.setParams(callsign, speed, frequency, lowLevel); + return m_callsign.setParams(callsign, speed, frequency, level); } uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) diff --git a/FM.h b/FM.h index bcea9ab..b96ccc8 100644 --- a/FM.h +++ b/FM.h @@ -47,7 +47,7 @@ public: void reset(); - uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); + uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t level, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); diff --git a/SerialPort.cpp b/SerialPort.cpp index 63313e8..45624da 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200417 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200418 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -365,26 +365,25 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) { - if (length < 8U) + if (length < 7U) 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]; + uint8_t level = data[4U]; - bool callAtStart = (data[6U] & 0x01U) == 0x01U; - bool callAtEnd = (data[6U] & 0x02U) == 0x02U; + bool callAtStart = (data[5U] & 0x01U) == 0x01U; + bool callAtEnd = (data[5U] & 0x02U) == 0x02U; char callsign[50U]; uint8_t n = 0U; - for (uint8_t i = 7U; i < length; i++, n++) + for (uint8_t i = 6U; i < length; i++, n++) callsign[n] = data[i]; callsign[n] = '\0'; - return fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd); + return fm.setCallsign(callsign, speed, frequency, time, holdoff, level, callAtStart, callAtEnd); } uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) From c55002534e15a94cfad7a6c62da9ad46cb714bb2 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 13:55:34 +0100 Subject: [PATCH 023/119] Rename the Goertzel class to CTCSSRX. --- FM.cpp | 14 ++++++++------ FM.h | 6 +++--- FMGoertzel.cpp => FMCTCSSRX.cpp | 10 ++++++---- FMGoertzel.h => FMCTCSSRX.h | 10 +++++----- 4 files changed, 22 insertions(+), 18 deletions(-) rename FMGoertzel.cpp => FMCTCSSRX.cpp (80%) rename FMGoertzel.h => FMCTCSSRX.h (86%) diff --git a/FM.cpp b/FM.cpp index 4ff0a6a..69af344 100644 --- a/FM.cpp +++ b/FM.cpp @@ -44,8 +44,8 @@ m_filter(), m_filterState(), m_callsign(), m_rfAck(), -m_goertzel(), -m_ctcss(), +m_ctcssRX(), +m_ctcssTX(), m_timeoutTone(), m_state(FS_LISTENING), m_callsignAtStart(false), @@ -67,7 +67,7 @@ m_hangTimer() void CFM::samples(q15_t* samples, uint8_t length) { - bool validSignal = m_goertzel.process(samples, length); + bool validSignal = m_ctcssRX.process(samples, length); stateMachine(validSignal, length); @@ -92,7 +92,7 @@ void CFM::samples(q15_t* samples, uint8_t length) q15_t output[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_filter, samples, output, length); - m_ctcss.getAudio(output, length); + m_ctcssTX.getAudio(output, length); io.write(STATE_FM, output, length); } @@ -137,9 +137,11 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_timeoutTone.setParams(timeoutLevel); - m_goertzel.setParams(ctcssFrequency, ctcssThreshold); + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold); + if (ret != 0U) + return ret; - return m_ctcss.setParams(ctcssFrequency, ctcssLevel); + return m_ctcssTX.setParams(ctcssFrequency, ctcssLevel); } void CFM::stateMachine(bool validSignal, uint8_t length) diff --git a/FM.h b/FM.h index b96ccc8..c788904 100644 --- a/FM.h +++ b/FM.h @@ -21,7 +21,7 @@ #include "Config.h" -#include "FMGoertzel.h" +#include "FMCTCSSRX.h" #include "FMCTCSSTX.h" #include "FMTimeout.h" #include "FMKeyer.h" @@ -56,8 +56,8 @@ private: q15_t m_filterState[230U]; // NoTaps + BlockSize - 1, 201 + 20 - 1 plus some spare CFMKeyer m_callsign; CFMKeyer m_rfAck; - CFMGoertzel m_goertzel; - CFMCTCSSTX m_ctcss; + CFMCTCSSRX m_ctcssRX; + CFMCTCSSTX m_ctcssTX; CFMTimeout m_timeoutTone; FM_STATE m_state; bool m_callsignAtStart; diff --git a/FMGoertzel.cpp b/FMCTCSSRX.cpp similarity index 80% rename from FMGoertzel.cpp rename to FMCTCSSRX.cpp index 787047a..b945ea4 100644 --- a/FMGoertzel.cpp +++ b/FMCTCSSRX.cpp @@ -18,16 +18,18 @@ #include "Config.h" #include "Globals.h" -#include "FMGoertzel.h" +#include "FMCTCSSRX.h" -CFMGoertzel::CFMGoertzel() +CFMCTCSSRX::CFMCTCSSRX() { } -void CFMGoertzel::setParams(uint8_t frequency, uint8_t threshold) +uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { + return 0U; } -bool CFMGoertzel::process(const q15_t* samples, uint8_t length) +bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) { + return false; } diff --git a/FMGoertzel.h b/FMCTCSSRX.h similarity index 86% rename from FMGoertzel.h rename to FMCTCSSRX.h index 5d0330e..34c6e3c 100644 --- a/FMGoertzel.h +++ b/FMCTCSSRX.h @@ -16,16 +16,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#if !defined(FMGoertzel_H) -#define FMGoertzel_H +#if !defined(FMCTCSSRX_H) +#define FMCTCSSRX_H #include "Config.h" -class CFMGoertzel { +class CFMCTCSSRX { public: - CFMGoertzel(); + CFMCTCSSRX(); - void setParams(uint8_t frequency, uint8_t threshold); + uint8_t setParams(uint8_t frequency, uint8_t threshold); bool process(const q15_t* samples, uint8_t length); From 61ccd79c5f939a83c9380c2be3a0902b6b48117d Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 14:31:21 +0100 Subject: [PATCH 024/119] Set up the FM mode pins, partially. --- Config.h | 6 ++++-- IO.cpp | 10 ++++++++++ IO.h | 1 + IODue.cpp | 19 +++++++++++++++++-- IOSTM.cpp | 23 ++++++++++++++++++++--- IOSTM_CMSIS.cpp | 27 ++++++++++++++++++++++----- IOTeensy.cpp | 17 ++++++++++++++++- 7 files changed, 90 insertions(+), 13 deletions(-) diff --git a/Config.h b/Config.h index 9d62604..a0e249f 100644 --- a/Config.h +++ b/Config.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 @@ -84,5 +84,7 @@ // Use the D-Star and DMR LEDs for POCSAG // #define USE_ALTERNATE_POCSAG_LEDS -#endif +// Use the D-Star and YSF LEDs for FM +// #define USE_ALTERNATE_FM_LEDS +#endif diff --git a/IO.cpp b/IO.cpp index 93a8109..a26b6d1 100644 --- a/IO.cpp +++ b/IO.cpp @@ -169,23 +169,33 @@ void CIO::selfTest() delayInt(250); setP25Int(true); +#if !defined(USE_ALTERNATE_NXDN_LEDS) delayInt(250); setNXDNInt(true); +#endif +#if !defined(USE_ALTERNATE_POCSAG_LEDS) delayInt(250); setPOCSAGInt(true); +#endif +#if !defined(USE_ALTERNATE_FM_LEDS) delayInt(250); setFMInt(true); delayInt(250); setFMInt(false); +#endif +#if !defined(USE_ALTERNATE_POCSAG_LEDS) delayInt(250); setPOCSAGInt(false); +#endif +#if !defined(USE_ALTERNATE_NXDN_LEDS) delayInt(250); setNXDNInt(false); +#endif delayInt(250); setP25Int(false); diff --git a/IO.h b/IO.h index 1fea082..68ada02 100644 --- a/IO.h +++ b/IO.h @@ -120,6 +120,7 @@ private: void setP25Int(bool on); void setNXDNInt(bool on); void setPOCSAGInt(bool on); + void setFMInt(bool on); void delayInt(unsigned int dly); }; diff --git a/IODue.cpp b/IODue.cpp index 569c6e4..b2aec0a 100644 --- a/IODue.cpp +++ b/IODue.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 * @@ -35,6 +35,7 @@ #define PIN_P25 19 #define PIN_NXDN 20 #define PIN_POCSAG 4 +#define PIN_FM 5 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -50,6 +51,7 @@ #define PIN_P25 6 #define PIN_NXDN 5 #define PIN_POCSAG 4 +#define PIN_FM 3 #define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13) #define ADC_ISR_EOC_Chan ADC_ISR_EOC13 #define ADC_CDR_Chan 13 @@ -67,6 +69,7 @@ #define PIN_P25 6 #define PIN_NXDN 5 #define PIN_POCSAG 4 +#define PIN_FM 3 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -107,6 +110,9 @@ void CIO::initInt() #if !defined(USE_ALTERNATE_POCSAG_LEDS) pinMode(PIN_POCSAG, OUTPUT); #endif +#if !defined(USE_ALTERNATE_POCSAG_LEDS) + pinMode(PIN_FM, OUTPUT); +#endif #endif } @@ -258,10 +264,19 @@ void CIO::setPOCSAGInt(bool on) #endif } +void CIO::setFMInt(bool on) +{ +#if defined(USE_ALTERNATE_FM_LEDS) + digitalWrite(PIN_DSTAR, on ? HIGH : LOW); + digitalWrite(PIN_YSF, on ? HIGH : LOW); +#else + digitalWrite(PIN_FM, on ? HIGH : LOW); +#endif +} + void CIO::delayInt(unsigned int dly) { delay(dly); } #endif - diff --git a/IOSTM.cpp b/IOSTM.cpp index 9139953..1d565fe 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU - * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 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 @@ -42,8 +42,8 @@ POCSAG PB12 output CN10 Pin16 MDSTAR PC4 output CN10 Pin34 MDMR PC5 output CN10 Pin6 -MYSF PC2 output CN7 Pin35 -MP25 PC3 output CN7 Pin37 +MYSF PC2 output CN7 Pin35 +MP25 PC3 output CN7 Pin37 MNXDN PC6 output CN10 Pin4 MPOCSAG PC8 output CN10 Pin2 @@ -1438,6 +1438,23 @@ void CIO::setPOCSAGInt(bool on) #endif } +void CIO::setFMInt(bool on) +{ +#if defined(USE_ALTERNATE_FM_LEDS) + GPIO_WriteBit(PORT_DSTAR, PIN_DSTAR, on ? Bit_SET : Bit_RESET); + GPIO_WriteBit(PORT_YSF, PIN_YSF, on ? Bit_SET : Bit_RESET); +#if defined(MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && (defined(STM32F4_NUCLEO) || defined(STM32F722_RPT_HAT)) + GPIO_WriteBit(PORT_MDSTAR, PIN_MDSTAR, on ? Bit_SET : Bit_RESET); + GPIO_WriteBit(PORT_MYSF, PIN_MYSF, on ? Bit_SET : Bit_RESET); +#endif +#else + GPIO_WriteBit(PORT_FM, PIN_FM, on ? Bit_SET : Bit_RESET); +#if defined(MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && (defined(STM32F4_NUCLEO) || defined(STM32F722_RPT_HAT)) + GPIO_WriteBit(PORT_MFM, PIN_MFM, on ? Bit_SET : Bit_RESET); +#endif +#endif +} + // Simple delay function for STM32 // Example from: http://thehackerworkshop.com/?p=1209 void CIO::delayInt(unsigned int dly) diff --git a/IOSTM_CMSIS.cpp b/IOSTM_CMSIS.cpp index ce498d4..e3763b4 100644 --- a/IOSTM_CMSIS.cpp +++ b/IOSTM_CMSIS.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU - * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2017,2018,2020 by Jonathan Naylor G4KLX * Copyright (C) 2017 by Wojciech Krutnik N0CALL * * This program is free software; you can redistribute it and/or modify @@ -41,6 +41,7 @@ YSF PB8 output P25 PB9 output NXDN PB10 output POCSAG PB11 output +FM PB12 output RX PB0 analog input (ADC1_8) RSSI PB1 analog input (ADC2_9) @@ -84,6 +85,9 @@ USART1_RXD PA10 input (AF) #define PIN_POCSAG 11 #define PORT_POCSAG GPIOB #define BB_POCSAG *((bitband_t)BITBAND_PERIPH(&PORT_POCSAG->ODR, PIN_POCSAG)) +#define PIN_FM 12 +#define PORT_FM GPIOB +#define BB_FM *((bitband_t)BITBAND_PERIPH(&PORT_FM->ODR, PIN_FM)) #define PIN_RX 0 #define PIN_RX_ADC_CH 8 @@ -154,9 +158,9 @@ void GPIOConfigPin(GPIO_TypeDef *port_ptr, uint32_t pin, uint32_t mode_cnf_value #if defined(STM32F1_POG) void FancyLEDEffect() { - bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25}; + bitband_t foo[] = {&BB_LED, &BB_COSLED, &BB_PTT, &BB_DMR, &BB_DSTAR, &BB_YSF, &BB_P25, &BB_NXDN, &BB_POCSAG, &BB_FM}; - for(int i=0; i<7; i++){ + for(int i=0; i<10; i++){ *foo[i] = 0x01; } GPIOConfigPin(PORT_USART1_TXD, PIN_USART1_TXD, GPIO_CRL_MODE0_1); @@ -172,12 +176,12 @@ void FancyLEDEffect() *((bitband_t)BITBAND_PERIPH(&PORT_USART1_TXD->ODR, PIN_USART1_TXD)) = 0x01; *foo[0] = 0x01; - for(int i=1; i<7; i++){ + for(int i=1; i<10; i++){ delay(SystemCoreClock/1000*10); *foo[i-1] = 0x00; *foo[i] = 0x01; } - for(int i=5; i>=0; i--){ + for(int i=10; i>=0; i--){ delay(SystemCoreClock/1000*10); *foo[i+1] = 0x00; *foo[i] = 0x01; @@ -228,6 +232,9 @@ static inline void GPIOInit() #if !defined(USE_ALTERNATE_POCSAG_LEDS) GPIOConfigPin(PORT_POCSAG, PIN_POCSAG, GPIO_CRL_MODE0_1); #endif +#if !defined(USE_ALTERNATE_FM_LEDS) + GPIOConfigPin(PORT_FM, PIN_FM, GPIO_CRL_MODE0_1); +#endif GPIOConfigPin(PORT_RX, PIN_RX, 0); #if defined(SEND_RSSI_DATA) @@ -460,6 +467,16 @@ void CIO::setPOCSAGInt(bool on) #endif } +void CIO::setFMInt(bool on) +{ +#if defined(USE_ALTERNATE_FM_LEDS) + BB_DSTAR = !!on; + BB_YSF = !!on; +#else + BB_FM = !!on; +#endif +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/IOTeensy.cpp b/IOTeensy.cpp index 1d9aa79..aaece08 100644 --- a/IOTeensy.cpp +++ b/IOTeensy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 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 @@ -37,9 +37,11 @@ #if defined(__MK20DX256__) #define PIN_NXDN 2 #define PIN_POCSAG 3 +#define PIN_FM 4 #else #define PIN_NXDN 24 #define PIN_POCSAG 25 +#define PIN_FM 26 #endif #define PIN_ADC 5 // A0, Pin 14 #define PIN_RSSI 4 // Teensy 3.5/3.6, A16, Pin 35. Teensy 3.1/3.2, A17, Pin 28 @@ -76,6 +78,9 @@ void CIO::initInt() #if !defined(USE_ALTERNATE_POCSAG_LEDS) pinMode(PIN_POCSAG, OUTPUT); #endif +#if !defined(USE_ALTERNATE_FM_LEDS) + pinMode(PIN_FM, OUTPUT); +#endif #endif } @@ -243,6 +248,16 @@ void CIO::setPOCSAGInt(bool on) #endif } +void CIO::setFMInt(bool on) +{ +#if defined(USE_ALTERNATE_FM_LEDS) + digitalWrite(PIN_DSTAR, on ? HIGH : LOW); + digitalWrite(PIN_YSF, on ? HIGH : LOW); +#else + digitalWrite(PIN_FM, on ? HIGH : LOW); +#endif +} + void CIO::delayInt(unsigned int dly) { delay(dly); From c8a450b00d419c0126810a5e9c53f82f63d4d40c Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 15:51:49 +0100 Subject: [PATCH 025/119] Ensure all timers are stopped when we leave FM mode. --- FM.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/FM.cpp b/FM.cpp index 69af344..79e8cf8 100644 --- a/FM.cpp +++ b/FM.cpp @@ -184,6 +184,13 @@ void CFM::stateMachine(bool validSignal, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) { DEBUG1("Change to STATE_IDLE"); m_modemState = STATE_IDLE; + m_callsignTimer.stop(); + m_timeoutTimer.stop(); + m_holdoffTimer.stop(); + m_kerchunkTimer.stop(); + m_ackMinTimer.stop(); + m_ackDelayTimer.stop(); + m_hangTimer.stop(); } } } From 6d5b864cbaa1ce7d14291d460202bada71ba71c1 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 16:07:52 +0100 Subject: [PATCH 026/119] Start the Goertzel development. --- FMCTCSSRX.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++- FMCTCSSRX.h | 2 ++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index b945ea4..c2a28df 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -20,12 +20,83 @@ #include "Globals.h" #include "FMCTCSSRX.h" -CFMCTCSSRX::CFMCTCSSRX() +const struct CTCSS_TABLE { + uint8_t frequency; + q31_t coeffDivTwo; +} CTCSS_TABLE_DATA[] = { + { 67U, 2147475280}, + { 69U, 2147474696}, + { 71U, 2147474012}, + { 74U, 2147473330}, + { 77U, 2147472596}, + { 79U, 2147471807}, + { 82U, 2147470961}, + { 85U, 2147470053}, + { 88U, 2147469048}, + { 91U, 2147468042}, + { 94U, 2147466895}, + { 97U, 2147465964}, + {100U, 2147465007}, + {103U, 2147463679}, + {107U, 2147462226}, + {110U, 2147460722}, + {114U, 2147459081}, + {118U, 2147457339}, + {123U, 2147455446}, + {127U, 2147453440}, + {131U, 2147451266}, + {136U, 2147448916}, + {141U, 2147446430}, + {146U, 2147443804}, + {151U, 2147440919}, + {156U, 2147437875}, + {159U, 2147436046}, + {162U, 2147434605}, + {165U, 2147432590}, + {167U, 2147431098}, + {171U, 2147428948}, + {173U, 2147427340}, + {177U, 2147425049}, + {179U, 2147423318}, + {183U, 2147420879}, + {186U, 2147419018}, + {189U, 2147416424}, + {192U, 2147414356}, + {196U, 2147411597}, + {199U, 2147409456}, + {203U, 2147406451}, + {206U, 2147404158}, + {210U, 2147400892}, + {218U, 2147394977}, + {225U, 2147388689}, + {229U, 2147385807}, + {233U, 2147381925}, + {241U, 2147374659}, + {250U, 2147366861}, + {254U, 2147363288}}; + +const uint8_t CTCSS_TABLE_DATA_LEN = 50U; + +CFMCTCSSRX::CFMCTCSSRX() : +m_coeffDivTwo(0), +m_threshold(0U) { } uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { + for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { + if (CTCSS_TABLE_DATA[i].frequency == frequency) { + m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; + break; + } + } + + if (m_coeffDivTwo == 0) + return 4U; + + m_threshold = threshold; + return 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 34c6e3c..5e08ee6 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -30,6 +30,8 @@ public: bool process(const q15_t* samples, uint8_t length); private: + q31_t m_coeffDivTwo; + uint8_t m_threshold; }; #endif From ebdba2bdb9fb26a0cbb74cfed4bf17485085104d Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 16:48:30 +0100 Subject: [PATCH 027/119] Convert to using floats. --- FM.cpp | 1 + FMCTCSSRX.cpp | 151 ++++++++++++++++++++++++++++++-------------------- FMCTCSSRX.h | 12 +++- 3 files changed, 102 insertions(+), 62 deletions(-) diff --git a/FM.cpp b/FM.cpp index 79e8cf8..0b5d86c 100644 --- a/FM.cpp +++ b/FM.cpp @@ -103,6 +103,7 @@ void CFM::process() void CFM::reset() { + m_ctcssRX.reset(); } uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t level, bool callsignAtStart, bool callsignAtEnd) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index c2a28df..e57cfe6 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -21,65 +21,72 @@ #include "FMCTCSSRX.h" const struct CTCSS_TABLE { - uint8_t frequency; - q31_t coeffDivTwo; + uint8_t frequency; + float32_t coeff; } CTCSS_TABLE_DATA[] = { - { 67U, 2147475280}, - { 69U, 2147474696}, - { 71U, 2147474012}, - { 74U, 2147473330}, - { 77U, 2147472596}, - { 79U, 2147471807}, - { 82U, 2147470961}, - { 85U, 2147470053}, - { 88U, 2147469048}, - { 91U, 2147468042}, - { 94U, 2147466895}, - { 97U, 2147465964}, - {100U, 2147465007}, - {103U, 2147463679}, - {107U, 2147462226}, - {110U, 2147460722}, - {114U, 2147459081}, - {118U, 2147457339}, - {123U, 2147455446}, - {127U, 2147453440}, - {131U, 2147451266}, - {136U, 2147448916}, - {141U, 2147446430}, - {146U, 2147443804}, - {151U, 2147440919}, - {156U, 2147437875}, - {159U, 2147436046}, - {162U, 2147434605}, - {165U, 2147432590}, - {167U, 2147431098}, - {171U, 2147428948}, - {173U, 2147427340}, - {177U, 2147425049}, - {179U, 2147423318}, - {183U, 2147420879}, - {186U, 2147419018}, - {189U, 2147416424}, - {192U, 2147414356}, - {196U, 2147411597}, - {199U, 2147409456}, - {203U, 2147406451}, - {206U, 2147404158}, - {210U, 2147400892}, - {218U, 2147394977}, - {225U, 2147388689}, - {229U, 2147385807}, - {233U, 2147381925}, - {241U, 2147374659}, - {250U, 2147366861}, - {254U, 2147363288}}; + { 67U, 1.999692}, + { 69U, 1.999671}, + { 71U, 1.999646}, + { 74U, 1.999621}, + { 77U, 1.999594}, + { 79U, 1.999565}, + { 82U, 1.999534}, + { 85U, 1.999500}, + { 88U, 1.999463}, + { 91U, 1.999426}, + { 94U, 1.999384}, + { 97U, 1.999350}, + {100U, 1.999315}, + {103U, 1.999266}, + {107U, 1.999212}, + {110U, 1.999157}, + {114U, 1.999097}, + {118U, 1.999033}, + {123U, 1.998963}, + {127U, 1.998889}, + {131U, 1.998810}, + {136U, 1.998723}, + {141U, 1.998632}, + {146U, 1.998535}, + {151U, 1.998429}, + {156U, 1.998317}, + {159U, 1.998250}, + {162U, 1.998197}, + {165U, 1.998123}, + {167U, 1.998068}, + {171U, 1.997989}, + {173U, 1.997930}, + {177U, 1.997846}, + {179U, 1.997782}, + {183U, 1.997693}, + {186U, 1.997624}, + {189U, 1.997529}, + {192U, 1.997453}, + {196U, 1.997351}, + {199U, 1.997273}, + {203U, 1.997162}, + {206U, 1.997078}, + {210U, 1.996958}, + {218U, 1.996741}, + {225U, 1.996510}, + {229U, 1.996404}, + {233U, 1.996261}, + {241U, 1.995994}, + {250U, 1.995708}, + {254U, 1.995576}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; +// 4Hz bandwidth +const uint16_t N = 24000U / 4U; + CFMCTCSSRX::CFMCTCSSRX() : -m_coeffDivTwo(0), -m_threshold(0U) +m_coeff(0.0), +m_threshold(0.0), +m_count(0U), +m_q0(0.0), +m_q1(0.0), +m_result(false) { } @@ -87,20 +94,46 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { if (CTCSS_TABLE_DATA[i].frequency == frequency) { - m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; + m_coeff = CTCSS_TABLE_DATA[i].coeff; break; } } - if (m_coeffDivTwo == 0) + if (m_coeff == 0.0) return 4U; - m_threshold = threshold; + m_threshold = float32_t(threshold * threshold); return 0U; } -bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) +bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) { - return false; + float32_t data[RX_BLOCK_SIZE]; + ::arm_q15_to_float(samples, data, length); + + for (unsigned int i = 0U; i < length; i++) { + float32_t q2 = m_q1; + m_q1 = m_q0; + m_q0 = m_coeff * m_q1 - q2 + data[i]; + + m_count++; + if (m_count == N) { + float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; + m_result = value >= m_threshold; + m_count = 0U; + m_q0 = 0.0F; + m_q1 = 0.0F; + } + } + + return m_result; +} + +void CFMCTCSSRX::reset() +{ + m_q0 = 0.0; + m_q1 = 0.0; + m_result = false; + m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 5e08ee6..14e405a 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -27,11 +27,17 @@ public: uint8_t setParams(uint8_t frequency, uint8_t threshold); - bool process(const q15_t* samples, uint8_t length); + bool process(q15_t* samples, uint8_t length); + + void reset(); private: - q31_t m_coeffDivTwo; - uint8_t m_threshold; + float32_t m_coeff; + float32_t m_threshold; + uint16_t m_count; + float m_q0; + float m_q1; + bool m_result; }; #endif From 85816d5becf20037fd1486c80bde29d19a21e0fc Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 18 Apr 2020 16:59:25 +0100 Subject: [PATCH 028/119] Re-add COS. --- FM.cpp | 6 +++--- FM.h | 2 +- IO.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 0b5d86c..4528d90 100644 --- a/FM.cpp +++ b/FM.cpp @@ -65,11 +65,11 @@ m_hangTimer() m_filter.pCoeffs = FILTER_COEFFS; } -void CFM::samples(q15_t* samples, uint8_t length) +void CFM::samples(bool cos, q15_t* samples, uint8_t length) { - bool validSignal = m_ctcssRX.process(samples, length); + bool validCTCSS = m_ctcssRX.process(samples, length); - stateMachine(validSignal, length); + stateMachine(validCTCSS && cos, length); if (m_modemState != STATE_FM) return; diff --git a/FM.h b/FM.h index c788904..26d18d7 100644 --- a/FM.h +++ b/FM.h @@ -41,7 +41,7 @@ class CFM { public: CFM(); - void samples(q15_t* samples, uint8_t length); + void samples(bool cos, q15_t* samples, uint8_t length); void process(); diff --git a/IO.cpp b/IO.cpp index a26b6d1..ceae6ab 100644 --- a/IO.cpp +++ b/IO.cpp @@ -370,7 +370,8 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - fm.samples(FMVals, RX_BLOCK_SIZE); + bool cos = getCOSInt(); + fm.samples(cos, FMVals, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -443,7 +444,8 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - fm.samples(FMVals, RX_BLOCK_SIZE); + bool cos = getCOSInt(); + fm.samples(cos, FMVals, RX_BLOCK_SIZE); } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); From f7e5c2436c875652b7609a638c00f688cec13441 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 16:52:11 +0100 Subject: [PATCH 029/119] Convert the Goertzel algorithm to Q15 and Q31. --- FMCTCSSRX.cpp | 164 +++++++++++++++++++++++++++++--------------------- FMCTCSSRX.h | 10 +-- 2 files changed, 99 insertions(+), 75 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index e57cfe6..9525ae9 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -21,59 +21,59 @@ #include "FMCTCSSRX.h" const struct CTCSS_TABLE { - uint8_t frequency; - float32_t coeff; + uint8_t frequency; + q31_t coeffDivTwo; } CTCSS_TABLE_DATA[] = { - { 67U, 1.999692}, - { 69U, 1.999671}, - { 71U, 1.999646}, - { 74U, 1.999621}, - { 77U, 1.999594}, - { 79U, 1.999565}, - { 82U, 1.999534}, - { 85U, 1.999500}, - { 88U, 1.999463}, - { 91U, 1.999426}, - { 94U, 1.999384}, - { 97U, 1.999350}, - {100U, 1.999315}, - {103U, 1.999266}, - {107U, 1.999212}, - {110U, 1.999157}, - {114U, 1.999097}, - {118U, 1.999033}, - {123U, 1.998963}, - {127U, 1.998889}, - {131U, 1.998810}, - {136U, 1.998723}, - {141U, 1.998632}, - {146U, 1.998535}, - {151U, 1.998429}, - {156U, 1.998317}, - {159U, 1.998250}, - {162U, 1.998197}, - {165U, 1.998123}, - {167U, 1.998068}, - {171U, 1.997989}, - {173U, 1.997930}, - {177U, 1.997846}, - {179U, 1.997782}, - {183U, 1.997693}, - {186U, 1.997624}, - {189U, 1.997529}, - {192U, 1.997453}, - {196U, 1.997351}, - {199U, 1.997273}, - {203U, 1.997162}, - {206U, 1.997078}, - {210U, 1.996958}, - {218U, 1.996741}, - {225U, 1.996510}, - {229U, 1.996404}, - {233U, 1.996261}, - {241U, 1.995994}, - {250U, 1.995708}, - {254U, 1.995576}}; + { 67U, 2147153298}, + { 69U, 2147130228}, + { 71U, 2147103212}, + { 74U, 2147076297}, + { 77U, 2147047330}, + { 79U, 2147016195}, + { 82U, 2146982775}, + { 85U, 2146946945}, + { 88U, 2146907275}, + { 91U, 2146867538}, + { 94U, 2146822298}, + { 97U, 2146785526}, + {100U, 2146747759}, + {103U, 2146695349}, + {107U, 2146637984}, + {110U, 2146578604}, + {114U, 2146513835}, + {118U, 2146445080}, + {123U, 2146370355}, + {127U, 2146291161}, + {131U, 2146205372}, + {136U, 2146112589}, + {141U, 2146014479}, + {146U, 2145910829}, + {151U, 2145796971}, + {156U, 2145676831}, + {159U, 2145604646}, + {162U, 2145547790}, + {165U, 2145468230}, + {167U, 2145409363}, + {171U, 2145324517}, + {173U, 2145261046}, + {177U, 2145170643}, + {179U, 2145102321}, + {183U, 2145006080}, + {186U, 2144932648}, + {189U, 2144830280}, + {192U, 2144748638}, + {196U, 2144639788}, + {199U, 2144555290}, + {203U, 2144436713}, + {206U, 2144346237}, + {210U, 2144217348}, + {218U, 2143983951}, + {225U, 2143735870}, + {229U, 2143622139}, + {233U, 2143469001}, + {241U, 2143182299}, + {250U, 2142874683}, + {254U, 2142733729}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; @@ -81,11 +81,11 @@ const uint8_t CTCSS_TABLE_DATA_LEN = 50U; const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : -m_coeff(0.0), -m_threshold(0.0), +m_coeffDivTwo(0), +m_threshold(0U), m_count(0U), -m_q0(0.0), -m_q1(0.0), +m_q0(0), +m_q1(0), m_result(false) { } @@ -94,36 +94,60 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { if (CTCSS_TABLE_DATA[i].frequency == frequency) { - m_coeff = CTCSS_TABLE_DATA[i].coeff; + m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; break; } } - if (m_coeff == 0.0) + if (m_coeffDivTwo == 0) return 4U; - m_threshold = float32_t(threshold * threshold); + m_threshold = threshold * threshold; return 0U; } -bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) +bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) { - float32_t data[RX_BLOCK_SIZE]; - ::arm_q15_to_float(samples, data, length); - for (unsigned int i = 0U; i < length; i++) { - float32_t q2 = m_q1; + q31_t q2 = m_q1; m_q1 = m_q0; - m_q0 = m_coeff * m_q1 - q2 + data[i]; + + // Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1 + q63_t t1 = m_coeffDivTwo * m_q1; + q31_t t2 = __SSAT(t1 >> 32, 31); + q31_t t3 = t2 * 2; + + // Convert input data to Q31 from Q15 + q31_t t4 = __SSAT(samples[i] << 16, 31); + + // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + samples[i] + m_q0 = t3 - q2 + t4; m_count++; if (m_count == N) { - float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; + // Q31 multiplication, t2 = m_q0 * m_q0 + q63_t t1 = m_q0 * m_q0; + q31_t t2 = __SSAT(t1 >> 32, 31); + + // Q31 multiplication, t4 = m_q0 * m_q0 + q63_t t3 = m_q1 * m_q1; + q31_t t4 = __SSAT(t3 >> 32, 31); + + // Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2 + q63_t t5 = m_q0 * m_q1; + q31_t t6 = __SSAT(t5 >> 32, 31); + q63_t t7 = t6 * m_coeffDivTwo; + q31_t t8 = __SSAT(t7 >> 32, 31); + q31_t t9 = t8 * 2; + + // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 + q31_t value = t2 + t4 - t9; + m_result = value >= m_threshold; m_count = 0U; - m_q0 = 0.0F; - m_q1 = 0.0F; + m_q0 = 0; + m_q1 = 0; } } @@ -132,8 +156,8 @@ bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) void CFMCTCSSRX::reset() { - m_q0 = 0.0; - m_q1 = 0.0; + m_q0 = 0; + m_q1 = 0; m_result = false; m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 14e405a..b6030f5 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -27,16 +27,16 @@ public: uint8_t setParams(uint8_t frequency, uint8_t threshold); - bool process(q15_t* samples, uint8_t length); + bool process(const q15_t* samples, uint8_t length); void reset(); private: - float32_t m_coeff; - float32_t m_threshold; + q31_t m_coeffDivTwo; + uint16_t m_threshold; uint16_t m_count; - float m_q0; - float m_q1; + q31_t m_q0; + q31_t m_q1; bool m_result; }; From a17bd9cc1d636b58d8d0fc6f0e6bf8e020280ed9 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 17:37:24 +0100 Subject: [PATCH 030/119] Don't scale up the sample values. --- FMCTCSSRX.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 9525ae9..270ee6f 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -118,11 +118,8 @@ bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) q31_t t2 = __SSAT(t1 >> 32, 31); q31_t t3 = t2 * 2; - // Convert input data to Q31 from Q15 - q31_t t4 = __SSAT(samples[i] << 16, 31); - // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + samples[i] - m_q0 = t3 - q2 + t4; + m_q0 = t3 - q2 + samples[i]; m_count++; if (m_count == N) { From 6575cba9821158ae247e3a7db51e2666105a0082 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 17:59:04 +0100 Subject: [PATCH 031/119] Rescale the coefficient. --- FMCTCSSRX.cpp | 126 +++++++++++++++++++++++++------------------------- FMCTCSSRX.h | 2 +- 2 files changed, 63 insertions(+), 65 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 270ee6f..9391ceb 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -22,58 +22,58 @@ const struct CTCSS_TABLE { uint8_t frequency; - q31_t coeffDivTwo; + q31_t coeff; } CTCSS_TABLE_DATA[] = { - { 67U, 2147153298}, - { 69U, 2147130228}, - { 71U, 2147103212}, - { 74U, 2147076297}, - { 77U, 2147047330}, - { 79U, 2147016195}, - { 82U, 2146982775}, - { 85U, 2146946945}, - { 88U, 2146907275}, - { 91U, 2146867538}, - { 94U, 2146822298}, - { 97U, 2146785526}, - {100U, 2146747759}, - {103U, 2146695349}, - {107U, 2146637984}, - {110U, 2146578604}, - {114U, 2146513835}, - {118U, 2146445080}, - {123U, 2146370355}, - {127U, 2146291161}, - {131U, 2146205372}, - {136U, 2146112589}, - {141U, 2146014479}, - {146U, 2145910829}, - {151U, 2145796971}, - {156U, 2145676831}, - {159U, 2145604646}, - {162U, 2145547790}, - {165U, 2145468230}, - {167U, 2145409363}, - {171U, 2145324517}, - {173U, 2145261046}, - {177U, 2145170643}, - {179U, 2145102321}, - {183U, 2145006080}, - {186U, 2144932648}, - {189U, 2144830280}, - {192U, 2144748638}, - {196U, 2144639788}, - {199U, 2144555290}, - {203U, 2144436713}, - {206U, 2144346237}, - {210U, 2144217348}, - {218U, 2143983951}, - {225U, 2143735870}, - {229U, 2143622139}, - {233U, 2143469001}, - {241U, 2143182299}, - {250U, 2142874683}, - {254U, 2142733729}}; + { 67U, 131052}, + { 69U, 131051}, + { 71U, 131049}, + { 74U, 131048}, + { 77U, 131046}, + { 79U, 131044}, + { 82U, 131042}, + { 85U, 131040}, + { 88U, 131037}, + { 91U, 131035}, + { 94U, 131032}, + { 97U, 131030}, + {100U, 131028}, + {103U, 131024}, + {107U, 131021}, + {110U, 131017}, + {114U, 131013}, + {118U, 131009}, + {123U, 131005}, + {127U, 131000}, + {131U, 130994}, + {136U, 130989}, + {141U, 130983}, + {146U, 130977}, + {151U, 130970}, + {156U, 130962}, + {159U, 130958}, + {162U, 130954}, + {165U, 130949}, + {167U, 130946}, + {171U, 130941}, + {173U, 130937}, + {177U, 130931}, + {179U, 130927}, + {183U, 130921}, + {186U, 130917}, + {189U, 130911}, + {192U, 130906}, + {196U, 130899}, + {199U, 130894}, + {203U, 130887}, + {206U, 130881}, + {210U, 130873}, + {218U, 130859}, + {225U, 130844}, + {229U, 130837}, + {233U, 130827}, + {241U, 130810}, + {250U, 130791}, + {254U, 130783}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; @@ -81,7 +81,7 @@ const uint8_t CTCSS_TABLE_DATA_LEN = 50U; const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : -m_coeffDivTwo(0), +m_coeff(0), m_threshold(0U), m_count(0U), m_q0(0), @@ -94,12 +94,12 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { if (CTCSS_TABLE_DATA[i].frequency == frequency) { - m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; + m_coeff = CTCSS_TABLE_DATA[i].coeff; break; } } - if (m_coeffDivTwo == 0) + if (m_coeff == 0) return 4U; m_threshold = threshold * threshold; @@ -113,13 +113,12 @@ bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) q31_t q2 = m_q1; m_q1 = m_q0; - // Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1 - q63_t t1 = m_coeffDivTwo * m_q1; + // Q31 multiplication, t2 = m_coeff * m_q1 + q63_t t1 = m_coeff * m_q1; q31_t t2 = __SSAT(t1 >> 32, 31); - q31_t t3 = t2 * 2; - // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + samples[i] - m_q0 = t3 - q2 + samples[i]; + // m_q0 = m_coeff * m_q1 - q2 + samples[i] + m_q0 = t2 - q2 + samples[i]; m_count++; if (m_count == N) { @@ -131,15 +130,14 @@ bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) q63_t t3 = m_q1 * m_q1; q31_t t4 = __SSAT(t3 >> 32, 31); - // Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2 + // Q31 multiplication, t8 = m_q0 * m_q1 * m_coeff q63_t t5 = m_q0 * m_q1; q31_t t6 = __SSAT(t5 >> 32, 31); - q63_t t7 = t6 * m_coeffDivTwo; + q63_t t7 = t6 * m_coeff; q31_t t8 = __SSAT(t7 >> 32, 31); - q31_t t9 = t8 * 2; - // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 - q31_t value = t2 + t4 - t9; + // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff + q31_t value = t2 + t4 - t8; m_result = value >= m_threshold; m_count = 0U; diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index b6030f5..c7fcef9 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -32,7 +32,7 @@ public: void reset(); private: - q31_t m_coeffDivTwo; + q31_t m_coeff; uint16_t m_threshold; uint16_t m_count; q31_t m_q0; From ec202d7fb90d0490d4a3770506af35a16b840f4b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 13 Apr 2020 16:15:24 +0200 Subject: [PATCH 032/119] Add Goertzel --- Goertzel.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Goertzel.h | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Goertzel.cpp create mode 100644 Goertzel.h diff --git a/Goertzel.cpp b/Goertzel.cpp new file mode 100644 index 0000000..811fd97 --- /dev/null +++ b/Goertzel.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "Goertzel.h" + +CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : +m_min(0), +m_max(0), +m_processedSamplesCount(0), +m_n(n), +m_window(window),//Window should not be deleted by someone else +m_windowCorr(windowCorr) +{ + m_freqs[0] = f1; + m_freqs[1] = f2; + m_freqs[2] = f3; + + reset(); +} + +void CGoertzel::reset() +{ + ::memset(m_q1s, 0, sizeof(m_q1s)); + ::memset(m_q2s, 0, sizeof(m_q2s)); + m_processedSamplesCount = 0U; + m_max = 0U; + m_min = 0U; +} + +GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) +{ + int scalingFactor = (length / 2) * m_windowCorr; + unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; + GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; + + for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { + + if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; + if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; + + for(uint8_t i = 0; i < 3; i++) { + int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); + m_q2s[i] = m_q1s[i]; + m_q1s[i] = q0; + } + + m_processedSamplesCount++; + //we have collected enough samples, evaluate now, + if(m_processedSamplesCount == m_n) { + if(magnitudesComputed == GR_NOT_READY) { + //however if we already had collected enough samples only keep the magnitudes we computed the first time + int span = m_max - m_min; + for(uint8_t i = 0; i < 3; i++) { + int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic + int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; + + *(magnitudes[i]) = real * real + imag * imag; + } + + magnitudesComputed = GR_READY; + } + reset(); + } + } + + return magnitudesComputed; +} diff --git a/Goertzel.h b/Goertzel.h new file mode 100644 index 0000000..1b15db9 --- /dev/null +++ b/Goertzel.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(GOERTZEL_H) +#define GOERTZEL_H + +#include "Globals.h" + +typedef struct +{ + int sin, cos, coeff; +} TGoertzelParameters; + + +enum GOERTZEL_RESULT : uint8_t +{ + GR_NOT_READY = 0, + GR_READY = 1 +}; + +class CGoertzel { +public: + //f1,f2,f3 are natural frequencies + CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); + + void reset(); + GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); + +private: + TGoertzelParameters m_freqs[3]; + + q15_t m_min; + q15_t m_max; + + q15_t m_omegas[3]; + int m_sines[3]; + int m_cosines[3]; + int m_coeffs[3]; + + int m_q1s[3]; + int m_q2s[3]; + + uint16_t m_processedSamplesCount; + uint16_t m_n; + + const int* m_window; + int m_windowCorr; +}; + +#endif From 7435074c0fdad2c0b7e8f65553f9fc329b20b8ba Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 14 Apr 2020 12:16:07 +0200 Subject: [PATCH 033/119] add CTCSS decoder --- CTCSSDecoder.cpp | 46 ++++++++ CTCSSDecoder.h | 112 ++++++++++++++++++++ HannWindow.h | 268 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 CTCSSDecoder.cpp create mode 100644 CTCSSDecoder.h create mode 100644 HannWindow.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp new file mode 100644 index 0000000..8905b2b --- /dev/null +++ b/CTCSSDecoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "CTCSSDecoder.h" + +CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : +m_thresholdSquared(threshold * threshold), +m_hasCTCSS(false) +{ + m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); +} + + +void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) +{ + unsigned int f1MagSquared, f2MagSquared, f3MagSquared; + GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); + + if(result == GR_READY) { + // Goertzel says it has something for us, so checkt it + // make sure the strongest tone is the wanted one and not the neighbouring tone + m_hasCTCSS = f2MagSquared >= m_thresholdSquared + && f2MagSquared > f1MagSquared + && f2MagSquared > f3MagSquared; + } +} + +bool CCTCSSDEcoder::hasCTCSS() +{ + return m_hasCTCSS; +} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h new file mode 100644 index 0000000..cf9e5e4 --- /dev/null +++ b/CTCSSDecoder.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(CTCSSDECODER_H) +#define CTCSSDECODER_H + +#include "Globals.h" +#include "Goertzel.h" +#include "HannWindow.h" + +#define N_SAMPLES 12000 + +typedef struct +{ + TGoertzelParameters toneLow, tone, toneHi; +} TCTCSSTone; + + +/* + * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. + * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. + * We need in since intermediate values in goertzel will overflow Q15 + */ +const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; +const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; +const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; +const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; +const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; +const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; +const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; +const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; +const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; +const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; +const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; +const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; +const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; +const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; +const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; +const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; +const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; +const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; +const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; +const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; +const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; +const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; +const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; +const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; +const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; +const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; +const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; +const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; +const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; +const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; +const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; +const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; +const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; +const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; +const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; +const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; +const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; +const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; +const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; +const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; +const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; +const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; +const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; +const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; +const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; +const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; +const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; +const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; +const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; +const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; +const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; +const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; +const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; +const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; +const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; + + + +class CCTCSSDEcoder { +public: + //threshold must be 0.0 to 1.0; + CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); + + void samples(const q15_t* samples, uint8_t length); + bool hasCTCSS(); + +private: + unsigned int m_thresholdSquared; + bool m_hasCTCSS; + CGoertzel* m_goertzel; + +}; + +#endif diff --git a/HannWindow.h b/HannWindow.h new file mode 100644 index 0000000..4df9a5e --- /dev/null +++ b/HannWindow.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(HANNWINDOW_H) +#define HANNWINDOW_H + +/* Hann window value as Q15 yet stored into int to avoid overflowing during calculation */ + +const int HANN_WINDOW_CORR = 16383; + +const int HANN_WINDOW[12000] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22, +22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,39,39,40,40,41,42,42,43,43,44,45,45,46,47,47,48,49,49,50, +51,51,52,53,53,54,55,55,56,57,57,58,59,60,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,74,74,75,76,77,78,78,79,80,81,82,83,84,84,85,86,87,88,89, +90,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139, +140,141,142,144,145,146,147,148,149,150,152,153,154,155,156,157,159,160,161,162,163,165,166,167,168,170,171,172,173,175,176,177,178,180,181,182,183,185,186,187,189,190,191,192,194,195,196,198,199,200, +202,203,204,206,207,209,210,211,213,214,215,217,218,220,221,222,224,225,227,228,229,231,232,234,235,237,238,240,241,243,244,245,247,248,250,251,253,254,256,257,259,261,262,264,265,267,268,270,271,273, +274,276,278,279,281,282,284,285,287,289,290,292,293,295,297,298,300,302,303,305,307,308,310,312,313,315,317,318,320,322,323,325,327,328,330,332,334,335,337,339,340,342,344,346,347,349,351,353,355,356, +358,360,362,363,365,367,369,371,373,374,376,378,380,382,383,385,387,389,391,393,395,397,398,400,402,404,406,408,410,412,414,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451, +453,455,457,459,461,463,465,467,469,471,473,475,477,479,481,483,485,487,490,492,494,496,498,500,502,504,506,508,511,513,515,517,519,521,523,526,528,530,532,534,536,539,541,543,545,547,550,552,554,556, +558,561,563,565,567,570,572,574,576,579,581,583,585,588,590,592,594,597,599,601,604,606,608,611,613,615,618,620,622,625,627,629,632,634,636,639,641,643,646,648,651,653,655,658,660,663,665,668,670,672, +675,677,680,682,685,687,690,692,694,697,699,702,704,707,709,712,714,717,719,722,724,727,729,732,735,737,740,742,745,747,750,752,755,758,760,763,765,768,771,773,776,778,781,784,786,789,791,794,797,799, +802,805,807,810,813,815,818,821,823,826,829,831,834,837,840,842,845,848,850,853,856,859,861,864,867,870,872,875,878,881,883,886,889,892,895,897,900,903,906,909,911,914,917,920,923,926,928,931,934,937, +940,943,946,949,951,954,957,960,963,966,969,972,975,978,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085, +1088,1091,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1126,1129,1132,1135,1138,1141,1144,1148,1151,1154,1157,1160,1163,1167,1170,1173,1176,1179,1183,1186,1189,1192,1195,1199,1202,1205,1208,1211,1215,1218,1221,1224,1228,1231,1234,1238,1241,1244, +1247,1251,1254,1257,1261,1264,1267,1270,1274,1277,1280,1284,1287,1290,1294,1297,1300,1304,1307,1310,1314,1317,1321,1324,1327,1331,1334,1338,1341,1344,1348,1351,1355,1358,1361,1365,1368,1372,1375,1379,1382,1385,1389,1392,1396,1399,1403,1406,1410,1413, +1417,1420,1424,1427,1431,1434,1438,1441,1445,1448,1452,1455,1459,1462,1466,1470,1473,1477,1480,1484,1487,1491,1494,1498,1502,1505,1509,1512,1516,1520,1523,1527,1530,1534,1538,1541,1545,1549,1552,1556,1560,1563,1567,1571,1574,1578,1582,1585,1589,1593, +1596,1600,1604,1607,1611,1615,1619,1622,1626,1630,1633,1637,1641,1645,1648,1652,1656,1660,1663,1667,1671,1675,1679,1682,1686,1690,1694,1698,1701,1705,1709,1713,1717,1720,1724,1728,1732,1736,1740,1743,1747,1751,1755,1759,1763,1767,1770,1774,1778,1782, +1786,1790,1794,1798,1802,1806,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893,1897,1901,1905,1909,1913,1917,1921,1925,1929,1933,1937,1941,1945,1949,1953,1957,1961,1965,1969,1974,1978,1982, +1986,1990,1994,1998,2002,2006,2010,2015,2019,2023,2027,2031,2035,2039,2043,2048,2052,2056,2060,2064,2068,2073,2077,2081,2085,2089,2094,2098,2102,2106,2110,2115,2119,2123,2127,2131,2136,2140,2144,2148,2153,2157,2161,2165,2170,2174,2178,2183,2187,2191, +2195,2200,2204,2208,2213,2217,2221,2226,2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2273,2278,2282,2286,2291,2295,2299,2304,2308,2313,2317,2321,2326,2330,2335,2339,2343,2348,2352,2357,2361,2366,2370,2375,2379,2383,2388,2392,2397,2401,2406,2410, +2415,2419,2424,2428,2433,2437,2442,2446,2451,2455,2460,2464,2469,2473,2478,2482,2487,2492,2496,2501,2505,2510,2514,2519,2523,2528,2533,2537,2542,2546,2551,2556,2560,2565,2569,2574,2579,2583,2588,2592,2597,2602,2606,2611,2616,2620,2625,2630,2634,2639, +2644,2648,2653,2658,2662,2667,2672,2676,2681,2686,2691,2695,2700,2705,2709,2714,2719,2724,2728,2733,2738,2743,2747,2752,2757,2762,2766,2771,2776,2781,2786,2790,2795,2800,2805,2810,2814,2819,2824,2829,2834,2838,2843,2848,2853,2858,2863,2867,2872,2877, +2882,2887,2892,2897,2901,2906,2911,2916,2921,2926,2931,2936,2941,2945,2950,2955,2960,2965,2970,2975,2980,2985,2990,2995,3000,3005,3010,3015,3020,3024,3029,3034,3039,3044,3049,3054,3059,3064,3069,3074,3079,3084,3089,3094,3099,3104,3109,3114,3119,3125, +3130,3135,3140,3145,3150,3155,3160,3165,3170,3175,3180,3185,3190,3195,3201,3206,3211,3216,3221,3226,3231,3236,3241,3247,3252,3257,3262,3267,3272,3277,3282,3288,3293,3298,3303,3308,3313,3319,3324,3329,3334,3339,3345,3350,3355,3360,3365,3371,3376,3381, +3386,3391,3397,3402,3407,3412,3418,3423,3428,3433,3439,3444,3449,3454,3460,3465,3470,3476,3481,3486,3491,3497,3502,3507,3513,3518,3523,3529,3534,3539,3545,3550,3555,3561,3566,3571,3577,3582,3587,3593,3598,3603,3609,3614,3619,3625,3630,3636,3641,3646, +3652,3657,3663,3668,3673,3679,3684,3690,3695,3701,3706,3711,3717,3722,3728,3733,3739,3744,3750,3755,3761,3766,3771,3777,3782,3788,3793,3799,3804,3810,3815,3821,3826,3832,3837,3843,3848,3854,3860,3865,3871,3876,3882,3887,3893,3898,3904,3909,3915,3921, +3926,3932,3937,3943,3948,3954,3960,3965,3971,3976,3982,3988,3993,3999,4004,4010,4016,4021,4027,4033,4038,4044,4050,4055,4061,4067,4072,4078,4083,4089,4095,4101,4106,4112,4118,4123,4129,4135,4140,4146,4152,4157,4163,4169,4175,4180,4186,4192,4198,4203, +4209,4215,4220,4226,4232,4238,4243,4249,4255,4261,4267,4272,4278,4284,4290,4295,4301,4307,4313,4319,4324,4330,4336,4342,4348,4354,4359,4365,4371,4377,4383,4389,4394,4400,4406,4412,4418,4424,4430,4435,4441,4447,4453,4459,4465,4471,4477,4482,4488,4494, +4500,4506,4512,4518,4524,4530,4536,4542,4548,4553,4559,4565,4571,4577,4583,4589,4595,4601,4607,4613,4619,4625,4631,4637,4643,4649,4655,4661,4667,4673,4679,4685,4691,4697,4703,4709,4715,4721,4727,4733,4739,4745,4751,4757,4763,4769,4775,4781,4787,4793, +4800,4806,4812,4818,4824,4830,4836,4842,4848,4854,4860,4866,4873,4879,4885,4891,4897,4903,4909,4915,4921,4928,4934,4940,4946,4952,4958,4964,4971,4977,4983,4989,4995,5001,5008,5014,5020,5026,5032,5039,5045,5051,5057,5063,5070,5076,5082,5088,5094,5101, +5107,5113,5119,5125,5132,5138,5144,5150,5157,5163,5169,5175,5182,5188,5194,5201,5207,5213,5219,5226,5232,5238,5244,5251,5257,5263,5270,5276,5282,5289,5295,5301,5308,5314,5320,5327,5333,5339,5346,5352,5358,5365,5371,5377,5384,5390,5396,5403,5409,5415, +5422,5428,5435,5441,5447,5454,5460,5467,5473,5479,5486,5492,5499,5505,5511,5518,5524,5531,5537,5544,5550,5556,5563,5569,5576,5582,5589,5595,5602,5608,5614,5621,5627,5634,5640,5647,5653,5660,5666,5673,5679,5686,5692,5699,5705,5712,5718,5725,5731,5738, +5744,5751,5757,5764,5770,5777,5784,5790,5797,5803,5810,5816,5823,5829,5836,5843,5849,5856,5862,5869,5875,5882,5889,5895,5902,5908,5915,5922,5928,5935,5941,5948,5955,5961,5968,5974,5981,5988,5994,6001,6008,6014,6021,6028,6034,6041,6048,6054,6061,6067, +6074,6081,6088,6094,6101,6108,6114,6121,6128,6134,6141,6148,6154,6161,6168,6174,6181,6188,6195,6201,6208,6215,6222,6228,6235,6242,6248,6255,6262,6269,6275,6282,6289,6296,6302,6309,6316,6323,6330,6336,6343,6350,6357,6363,6370,6377,6384,6391,6397,6404, +6411,6418,6425,6431,6438,6445,6452,6459,6466,6472,6479,6486,6493,6500,6507,6513,6520,6527,6534,6541,6548,6555,6561,6568,6575,6582,6589,6596,6603,6610,6616,6623,6630,6637,6644,6651,6658,6665,6672,6679,6685,6692,6699,6706,6713,6720,6727,6734,6741,6748, +6755,6762,6769,6776,6783,6790,6796,6803,6810,6817,6824,6831,6838,6845,6852,6859,6866,6873,6880,6887,6894,6901,6908,6915,6922,6929,6936,6943,6950,6957,6964,6971,6978,6985,6992,6999,7006,7013,7020,7027,7035,7042,7049,7056,7063,7070,7077,7084,7091,7098, +7105,7112,7119,7126,7133,7140,7148,7155,7162,7169,7176,7183,7190,7197,7204,7211,7219,7226,7233,7240,7247,7254,7261,7268,7276,7283,7290,7297,7304,7311,7318,7326,7333,7340,7347,7354,7361,7368,7376,7383,7390,7397,7404,7411,7419,7426,7433,7440,7447,7455, +7462,7469,7476,7483,7491,7498,7505,7512,7519,7527,7534,7541,7548,7556,7563,7570,7577,7584,7592,7599,7606,7613,7621,7628,7635,7642,7650,7657,7664,7671,7679,7686,7693,7701,7708,7715,7722,7730,7737,7744,7752,7759,7766,7773,7781,7788,7795,7803,7810,7817, +7825,7832,7839,7847,7854,7861,7869,7876,7883,7891,7898,7905,7913,7920,7927,7935,7942,7949,7957,7964,7971,7979,7986,7993,8001,8008,8016,8023,8030,8038,8045,8052,8060,8067,8075,8082,8089,8097,8104,8112,8119,8126,8134,8141,8149,8156,8164,8171,8178,8186, +8193,8201,8208,8216,8223,8230,8238,8245,8253,8260,8268,8275,8283,8290,8297,8305,8312,8320,8327,8335,8342,8350,8357,8365,8372,8380,8387,8395,8402,8410,8417,8425,8432,8440,8447,8455,8462,8470,8477,8485,8492,8500,8507,8515,8522,8530,8537,8545,8552,8560, +8568,8575,8583,8590,8598,8605,8613,8620,8628,8635,8643,8651,8658,8666,8673,8681,8688,8696,8704,8711,8719,8726,8734,8742,8749,8757,8764,8772,8779,8787,8795,8802,8810,8817,8825,8833,8840,8848,8856,8863,8871,8878,8886,8894,8901,8909,8917,8924,8932,8940, +8947,8955,8962,8970,8978,8985,8993,9001,9008,9016,9024,9031,9039,9047,9054,9062,9070,9077,9085,9093,9100,9108,9116,9124,9131,9139,9147,9154,9162,9170,9177,9185,9193,9201,9208,9216,9224,9231,9239,9247,9255,9262,9270,9278,9285,9293,9301,9309,9316,9324, +9332,9340,9347,9355,9363,9371,9378,9386,9394,9402,9409,9417,9425,9433,9440,9448,9456,9464,9472,9479,9487,9495,9503,9511,9518,9526,9534,9542,9549,9557,9565,9573,9581,9588,9596,9604,9612,9620,9628,9635,9643,9651,9659,9667,9674,9682,9690,9698,9706,9714, +9721,9729,9737,9745,9753,9761,9769,9776,9784,9792,9800,9808,9816,9824,9831,9839,9847,9855,9863,9871,9879,9886,9894,9902,9910,9918,9926,9934,9942,9950,9957,9965,9973,9981,9989,9997,10005,10013,10021,10029,10036,10044,10052,10060,10068,10076,10084,10092,10100,10108, +10116,10124,10131,10139,10147,10155,10163,10171,10179,10187,10195,10203,10211,10219,10227,10235,10243,10251,10259,10267,10274,10282,10290,10298,10306,10314,10322,10330,10338,10346,10354,10362,10370,10378,10386,10394,10402,10410,10418,10426,10434,10442,10450,10458,10466,10474,10482,10490,10498,10506, +10514,10522,10530,10538,10546,10554,10562,10570,10578,10586,10594,10602,10610,10618,10626,10634,10642,10650,10658,10667,10675,10683,10691,10699,10707,10715,10723,10731,10739,10747,10755,10763,10771,10779,10787,10795,10803,10811,10820,10828,10836,10844,10852,10860,10868,10876,10884,10892,10900,10908, +10916,10925,10933,10941,10949,10957,10965,10973,10981,10989,10997,11006,11014,11022,11030,11038,11046,11054,11062,11070,11079,11087,11095,11103,11111,11119,11127,11135,11144,11152,11160,11168,11176,11184,11192,11200,11209,11217,11225,11233,11241,11249,11257,11266,11274,11282,11290,11298,11306,11315, +11323,11331,11339,11347,11355,11364,11372,11380,11388,11396,11404,11413,11421,11429,11437,11445,11453,11462,11470,11478,11486,11494,11503,11511,11519,11527,11535,11544,11552,11560,11568,11576,11585,11593,11601,11609,11617,11626,11634,11642,11650,11658,11667,11675,11683,11691,11699,11708,11716,11724, +11732,11741,11749,11757,11765,11774,11782,11790,11798,11806,11815,11823,11831,11839,11848,11856,11864,11872,11881,11889,11897,11905,11914,11922,11930,11938,11947,11955,11963,11971,11980,11988,11996,12005,12013,12021,12029,12038,12046,12054,12062,12071,12079,12087,12096,12104,12112,12120,12129,12137, +12145,12154,12162,12170,12178,12187,12195,12203,12212,12220,12228,12236,12245,12253,12261,12270,12278,12286,12295,12303,12311,12320,12328,12336,12344,12353,12361,12369,12378,12386,12394,12403,12411,12419,12428,12436,12444,12453,12461,12469,12478,12486,12494,12503,12511,12519,12528,12536,12544,12553, +12561,12569,12578,12586,12594,12603,12611,12619,12628,12636,12644,12653,12661,12670,12678,12686,12695,12703,12711,12720,12728,12736,12745,12753,12762,12770,12778,12787,12795,12803,12812,12820,12828,12837,12845,12854,12862,12870,12879,12887,12896,12904,12912,12921,12929,12937,12946,12954,12963,12971, +12979,12988,12996,13005,13013,13021,13030,13038,13047,13055,13063,13072,13080,13089,13097,13105,13114,13122,13131,13139,13147,13156,13164,13173,13181,13189,13198,13206,13215,13223,13232,13240,13248,13257,13265,13274,13282,13291,13299,13307,13316,13324,13333,13341,13350,13358,13366,13375,13383,13392, +13400,13409,13417,13425,13434,13442,13451,13459,13468,13476,13485,13493,13501,13510,13518,13527,13535,13544,13552,13561,13569,13577,13586,13594,13603,13611,13620,13628,13637,13645,13654,13662,13670,13679,13687,13696,13704,13713,13721,13730,13738,13747,13755,13764,13772,13781,13789,13797,13806,13814, +13823,13831,13840,13848,13857,13865,13874,13882,13891,13899,13908,13916,13925,13933,13942,13950,13959,13967,13976,13984,13992,14001,14009,14018,14026,14035,14043,14052,14060,14069,14077,14086,14094,14103,14111,14120,14128,14137,14145,14154,14162,14171,14179,14188,14196,14205,14213,14222,14230,14239, +14247,14256,14264,14273,14281,14290,14298,14307,14315,14324,14332,14341,14350,14358,14367,14375,14384,14392,14401,14409,14418,14426,14435,14443,14452,14460,14469,14477,14486,14494,14503,14511,14520,14528,14537,14545,14554,14563,14571,14580,14588,14597,14605,14614,14622,14631,14639,14648,14656,14665, +14673,14682,14690,14699,14708,14716,14725,14733,14742,14750,14759,14767,14776,14784,14793,14801,14810,14819,14827,14836,14844,14853,14861,14870,14878,14887,14895,14904,14912,14921,14930,14938,14947,14955,14964,14972,14981,14989,14998,15006,15015,15024,15032,15041,15049,15058,15066,15075,15083,15092, +15101,15109,15118,15126,15135,15143,15152,15160,15169,15178,15186,15195,15203,15212,15220,15229,15237,15246,15255,15263,15272,15280,15289,15297,15306,15314,15323,15332,15340,15349,15357,15366,15374,15383,15392,15400,15409,15417,15426,15434,15443,15451,15460,15469,15477,15486,15494,15503,15511,15520, +15529,15537,15546,15554,15563,15571,15580,15589,15597,15606,15614,15623,15631,15640,15649,15657,15666,15674,15683,15691,15700,15709,15717,15726,15734,15743,15751,15760,15769,15777,15786,15794,15803,15811,15820,15829,15837,15846,15854,15863,15871,15880,15889,15897,15906,15914,15923,15931,15940,15949, +15957,15966,15974,15983,15992,16000,16009,16017,16026,16034,16043,16052,16060,16069,16077,16086,16094,16103,16112,16120,16129,16137,16146,16155,16163,16172,16180,16189,16197,16206,16215,16223,16232,16240,16249,16257,16266,16275,16283,16292,16300,16309,16318,16326,16335,16343,16352,16360,16369,16378, +16386,16395,16403,16412,16420,16429,16438,16446,16455,16463,16472,16481,16489,16498,16506,16515,16523,16532,16541,16549,16558,16566,16575,16583,16592,16601,16609,16618,16626,16635,16644,16652,16661,16669,16678,16686,16695,16704,16712,16721,16729,16738,16746,16755,16764,16772,16781,16789,16798,16806, +16815,16824,16832,16841,16849,16858,16867,16875,16884,16892,16901,16909,16918,16927,16935,16944,16952,16961,16969,16978,16987,16995,17004,17012,17021,17029,17038,17047,17055,17064,17072,17081,17089,17098,17107,17115,17124,17132,17141,17149,17158,17167,17175,17184,17192,17201,17209,17218,17227,17235, +17244,17252,17261,17269,17278,17287,17295,17304,17312,17321,17329,17338,17346,17355,17364,17372,17381,17389,17398,17406,17415,17424,17432,17441,17449,17458,17466,17475,17483,17492,17501,17509,17518,17526,17535,17543,17552,17561,17569,17578,17586,17595,17603,17612,17620,17629,17638,17646,17655,17663, +17672,17680,17689,17697,17706,17714,17723,17732,17740,17749,17757,17766,17774,17783,17791,17800,17809,17817,17826,17834,17843,17851,17860,17868,17877,17885,17894,17903,17911,17920,17928,17937,17945,17954,17962,17971,17979,17988,17996,18005,18014,18022,18031,18039,18048,18056,18065,18073,18082,18090, +18099,18107,18116,18124,18133,18142,18150,18159,18167,18176,18184,18193,18201,18210,18218,18227,18235,18244,18252,18261,18269,18278,18286,18295,18304,18312,18321,18329,18338,18346,18355,18363,18372,18380,18389,18397,18406,18414,18423,18431,18440,18448,18457,18465,18474,18482,18491,18499,18508,18516, +18525,18533,18542,18550,18559,18567,18576,18584,18593,18601,18610,18618,18627,18635,18644,18652,18661,18669,18678,18686,18695,18703,18712,18720,18729,18737,18746,18754,18763,18771,18780,18788,18797,18805,18814,18822,18831,18839,18848,18856,18865,18873,18882,18890,18898,18907,18915,18924,18932,18941, +18949,18958,18966,18975,18983,18992,19000,19009,19017,19026,19034,19043,19051,19059,19068,19076,19085,19093,19102,19110,19119,19127,19136,19144,19153,19161,19169,19178,19186,19195,19203,19212,19220,19229,19237,19245,19254,19262,19271,19279,19288,19296,19305,19313,19321,19330,19338,19347,19355,19364, +19372,19381,19389,19397,19406,19414,19423,19431,19440,19448,19456,19465,19473,19482,19490,19499,19507,19515,19524,19532,19541,19549,19557,19566,19574,19583,19591,19600,19608,19616,19625,19633,19642,19650,19658,19667,19675,19684,19692,19700,19709,19717,19726,19734,19742,19751,19759,19768,19776,19784, +19793,19801,19810,19818,19826,19835,19843,19852,19860,19868,19877,19885,19893,19902,19910,19919,19927,19935,19944,19952,19960,19969,19977,19986,19994,20002,20011,20019,20027,20036,20044,20052,20061,20069,20078,20086,20094,20103,20111,20119,20128,20136,20144,20153,20161,20169,20178,20186,20194,20203, +20211,20220,20228,20236,20245,20253,20261,20270,20278,20286,20295,20303,20311,20320,20328,20336,20345,20353,20361,20369,20378,20386,20394,20403,20411,20419,20428,20436,20444,20453,20461,20469,20478,20486,20494,20502,20511,20519,20527,20536,20544,20552,20561,20569,20577,20585,20594,20602,20610,20619, +20627,20635,20643,20652,20660,20668,20677,20685,20693,20701,20710,20718,20726,20735,20743,20751,20759,20768,20776,20784,20792,20801,20809,20817,20825,20834,20842,20850,20858,20867,20875,20883,20891,20900,20908,20916,20924,20933,20941,20949,20957,20966,20974,20982,20990,20999,21007,21015,21023,21032, +21040,21048,21056,21064,21073,21081,21089,21097,21106,21114,21122,21130,21138,21147,21155,21163,21171,21179,21188,21196,21204,21212,21220,21229,21237,21245,21253,21261,21270,21278,21286,21294,21302,21310,21319,21327,21335,21343,21351,21360,21368,21376,21384,21392,21400,21409,21417,21425,21433,21441, +21449,21458,21466,21474,21482,21490,21498,21506,21515,21523,21531,21539,21547,21555,21563,21572,21580,21588,21596,21604,21612,21620,21629,21637,21645,21653,21661,21669,21677,21685,21694,21702,21710,21718,21726,21734,21742,21750,21758,21767,21775,21783,21791,21799,21807,21815,21823,21831,21839,21847, +21856,21864,21872,21880,21888,21896,21904,21912,21920,21928,21936,21944,21952,21961,21969,21977,21985,21993,22001,22009,22017,22025,22033,22041,22049,22057,22065,22073,22081,22089,22097,22106,22114,22122,22130,22138,22146,22154,22162,22170,22178,22186,22194,22202,22210,22218,22226,22234,22242,22250, +22258,22266,22274,22282,22290,22298,22306,22314,22322,22330,22338,22346,22354,22362,22370,22378,22386,22394,22402,22410,22418,22426,22434,22442,22450,22458,22466,22474,22482,22490,22498,22505,22513,22521,22529,22537,22545,22553,22561,22569,22577,22585,22593,22601,22609,22617,22625,22633,22641,22648, +22656,22664,22672,22680,22688,22696,22704,22712,22720,22728,22736,22743,22751,22759,22767,22775,22783,22791,22799,22807,22815,22822,22830,22838,22846,22854,22862,22870,22878,22885,22893,22901,22909,22917,22925,22933,22941,22948,22956,22964,22972,22980,22988,22996,23003,23011,23019,23027,23035,23043, +23050,23058,23066,23074,23082,23090,23097,23105,23113,23121,23129,23137,23144,23152,23160,23168,23176,23183,23191,23199,23207,23215,23222,23230,23238,23246,23254,23261,23269,23277,23285,23293,23300,23308,23316,23324,23331,23339,23347,23355,23362,23370,23378,23386,23393,23401,23409,23417,23424,23432, +23440,23448,23455,23463,23471,23479,23486,23494,23502,23510,23517,23525,23533,23540,23548,23556,23564,23571,23579,23587,23594,23602,23610,23618,23625,23633,23641,23648,23656,23664,23671,23679,23687,23694,23702,23710,23717,23725,23733,23740,23748,23756,23763,23771,23779,23786,23794,23802,23809,23817, +23825,23832,23840,23848,23855,23863,23870,23878,23886,23893,23901,23909,23916,23924,23931,23939,23947,23954,23962,23970,23977,23985,23992,24000,24008,24015,24023,24030,24038,24045,24053,24061,24068,24076,24083,24091,24098,24106,24114,24121,24129,24136,24144,24151,24159,24167,24174,24182,24189,24197, +24204,24212,24219,24227,24234,24242,24249,24257,24264,24272,24280,24287,24295,24302,24310,24317,24325,24332,24340,24347,24355,24362,24370,24377,24385,24392,24400,24407,24414,24422,24429,24437,24444,24452,24459,24467,24474,24482,24489,24497,24504,24512,24519,24526,24534,24541,24549,24556,24564,24571, +24578,24586,24593,24601,24608,24616,24623,24630,24638,24645,24653,24660,24667,24675,24682,24690,24697,24704,24712,24719,24727,24734,24741,24749,24756,24763,24771,24778,24786,24793,24800,24808,24815,24822,24830,24837,24844,24852,24859,24866,24874,24881,24888,24896,24903,24910,24918,24925,24932,24940, +24947,24954,24962,24969,24976,24984,24991,24998,25006,25013,25020,25027,25035,25042,25049,25057,25064,25071,25078,25086,25093,25100,25107,25115,25122,25129,25136,25144,25151,25158,25165,25173,25180,25187,25194,25202,25209,25216,25223,25231,25238,25245,25252,25259,25267,25274,25281,25288,25295,25303, +25310,25317,25324,25331,25339,25346,25353,25360,25367,25374,25382,25389,25396,25403,25410,25417,25425,25432,25439,25446,25453,25460,25468,25475,25482,25489,25496,25503,25510,25517,25525,25532,25539,25546,25553,25560,25567,25574,25581,25589,25596,25603,25610,25617,25624,25631,25638,25645,25652,25659, +25666,25674,25681,25688,25695,25702,25709,25716,25723,25730,25737,25744,25751,25758,25765,25772,25779,25786,25793,25800,25807,25814,25821,25828,25835,25842,25849,25856,25863,25870,25877,25884,25891,25898,25905,25912,25919,25926,25933,25940,25947,25954,25961,25968,25975,25982,25989,25996,26003,26010, +26017,26024,26031,26038,26044,26051,26058,26065,26072,26079,26086,26093,26100,26107,26114,26121,26127,26134,26141,26148,26155,26162,26169,26176,26182,26189,26196,26203,26210,26217,26224,26231,26237,26244,26251,26258,26265,26272,26278,26285,26292,26299,26306,26313,26319,26326,26333,26340,26347,26354, +26360,26367,26374,26381,26388,26394,26401,26408,26415,26421,26428,26435,26442,26449,26455,26462,26469,26476,26482,26489,26496,26503,26509,26516,26523,26530,26536,26543,26550,26557,26563,26570,26577,26583,26590,26597,26604,26610,26617,26624,26630,26637,26644,26650,26657,26664,26670,26677,26684,26691, +26697,26704,26711,26717,26724,26730,26737,26744,26750,26757,26764,26770,26777,26784,26790,26797,26803,26810,26817,26823,26830,26837,26843,26850,26856,26863,26870,26876,26883,26889,26896,26902,26909,26916,26922,26929,26935,26942,26948,26955,26962,26968,26975,26981,26988,26994,27001,27007,27014,27020, +27027,27033,27040,27046,27053,27059,27066,27073,27079,27086,27092,27098,27105,27111,27118,27124,27131,27137,27144,27150,27157,27163,27170,27176,27183,27189,27196,27202,27208,27215,27221,27228,27234,27241,27247,27253,27260,27266,27273,27279,27285,27292,27298,27305,27311,27317,27324,27330,27337,27343, +27349,27356,27362,27368,27375,27381,27388,27394,27400,27407,27413,27419,27426,27432,27438,27445,27451,27457,27464,27470,27476,27483,27489,27495,27501,27508,27514,27520,27527,27533,27539,27546,27552,27558,27564,27571,27577,27583,27589,27596,27602,27608,27614,27621,27627,27633,27639,27646,27652,27658, +27664,27671,27677,27683,27689,27695,27702,27708,27714,27720,27726,27733,27739,27745,27751,27757,27763,27770,27776,27782,27788,27794,27800,27807,27813,27819,27825,27831,27837,27843,27850,27856,27862,27868,27874,27880,27886,27892,27898,27905,27911,27917,27923,27929,27935,27941,27947,27953,27959,27965, +27972,27978,27984,27990,27996,28002,28008,28014,28020,28026,28032,28038,28044,28050,28056,28062,28068,28074,28080,28086,28092,28098,28104,28110,28116,28122,28128,28134,28140,28146,28152,28158,28164,28170,28176,28182,28188,28194,28200,28206,28212,28218,28223,28229,28235,28241,28247,28253,28259,28265, +28271,28277,28283,28288,28294,28300,28306,28312,28318,28324,28330,28336,28341,28347,28353,28359,28365,28371,28377,28382,28388,28394,28400,28406,28412,28417,28423,28429,28435,28441,28446,28452,28458,28464,28470,28475,28481,28487,28493,28499,28504,28510,28516,28522,28527,28533,28539,28545,28550,28556, +28562,28568,28573,28579,28585,28591,28596,28602,28608,28613,28619,28625,28631,28636,28642,28648,28653,28659,28665,28670,28676,28682,28687,28693,28699,28704,28710,28716,28721,28727,28733,28738,28744,28749,28755,28761,28766,28772,28778,28783,28789,28794,28800,28806,28811,28817,28822,28828,28834,28839, +28845,28850,28856,28861,28867,28872,28878,28884,28889,28895,28900,28906,28911,28917,28922,28928,28933,28939,28944,28950,28955,28961,28966,28972,28977,28983,28988,28994,28999,29005,29010,29016,29021,29027,29032,29038,29043,29048,29054,29059,29065,29070,29076,29081,29086,29092,29097,29103,29108,29113, +29119,29124,29130,29135,29140,29146,29151,29157,29162,29167,29173,29178,29183,29189,29194,29199,29205,29210,29215,29221,29226,29231,29237,29242,29247,29253,29258,29263,29269,29274,29279,29285,29290,29295,29300,29306,29311,29316,29321,29327,29332,29337,29342,29348,29353,29358,29363,29369,29374,29379, +29384,29390,29395,29400,29405,29410,29416,29421,29426,29431,29436,29442,29447,29452,29457,29462,29467,29473,29478,29483,29488,29493,29498,29504,29509,29514,29519,29524,29529,29534,29539,29545,29550,29555,29560,29565,29570,29575,29580,29585,29590,29595,29601,29606,29611,29616,29621,29626,29631,29636, +29641,29646,29651,29656,29661,29666,29671,29676,29681,29686,29691,29696,29701,29706,29711,29716,29721,29726,29731,29736,29741,29746,29751,29756,29761,29766,29771,29776,29781,29786,29791,29795,29800,29805,29810,29815,29820,29825,29830,29835,29840,29845,29849,29854,29859,29864,29869,29874,29879,29884, +29888,29893,29898,29903,29908,29913,29918,29922,29927,29932,29937,29942,29946,29951,29956,29961,29966,29970,29975,29980,29985,29990,29994,29999,30004,30009,30013,30018,30023,30028,30033,30037,30042,30047,30051,30056,30061,30066,30070,30075,30080,30084,30089,30094,30099,30103,30108,30113,30117,30122, +30127,30131,30136,30141,30145,30150,30155,30159,30164,30169,30173,30178,30182,30187,30192,30196,30201,30206,30210,30215,30219,30224,30229,30233,30238,30242,30247,30251,30256,30261,30265,30270,30274,30279,30283,30288,30292,30297,30301,30306,30311,30315,30320,30324,30329,30333,30338,30342,30347,30351, +30356,30360,30364,30369,30373,30378,30382,30387,30391,30396,30400,30405,30409,30413,30418,30422,30427,30431,30436,30440,30444,30449,30453,30458,30462,30466,30471,30475,30479,30484,30488,30493,30497,30501,30506,30510,30514,30519,30523,30527,30532,30536,30540,30545,30549,30553,30558,30562,30566,30570, +30575,30579,30583,30588,30592,30596,30600,30605,30609,30613,30617,30622,30626,30630,30634,30639,30643,30647,30651,30656,30660,30664,30668,30672,30677,30681,30685,30689,30693,30697,30702,30706,30710,30714,30718,30722,30727,30731,30735,30739,30743,30747,30751,30756,30760,30764,30768,30772,30776,30780, +30784,30788,30792,30797,30801,30805,30809,30813,30817,30821,30825,30829,30833,30837,30841,30845,30849,30853,30857,30861,30865,30869,30873,30877,30881,30885,30889,30893,30897,30901,30905,30909,30913,30917,30921,30925,30929,30933,30937,30941,30945,30949,30953,30957,30960,30964,30968,30972,30976,30980, +30984,30988,30992,30996,30999,31003,31007,31011,31015,31019,31023,31026,31030,31034,31038,31042,31046,31050,31053,31057,31061,31065,31069,31072,31076,31080,31084,31088,31091,31095,31099,31103,31106,31110,31114,31118,31121,31125,31129,31133,31136,31140,31144,31148,31151,31155,31159,31162,31166,31170, +31174,31177,31181,31185,31188,31192,31196,31199,31203,31207,31210,31214,31218,31221,31225,31228,31232,31236,31239,31243,31247,31250,31254,31257,31261,31265,31268,31272,31275,31279,31282,31286,31290,31293,31297,31300,31304,31307,31311,31314,31318,31321,31325,31329,31332,31336,31339,31343,31346,31350, +31353,31357,31360,31363,31367,31370,31374,31377,31381,31384,31388,31391,31395,31398,31401,31405,31408,31412,31415,31419,31422,31425,31429,31432,31436,31439,31442,31446,31449,31452,31456,31459,31463,31466,31469,31473,31476,31479,31483,31486,31489,31493,31496,31499,31503,31506,31509,31512,31516,31519, +31522,31526,31529,31532,31535,31539,31542,31545,31548,31552,31555,31558,31561,31565,31568,31571,31574,31577,31581,31584,31587,31590,31593,31597,31600,31603,31606,31609,31613,31616,31619,31622,31625,31628,31631,31635,31638,31641,31644,31647,31650,31653,31656,31660,31663,31666,31669,31672,31675,31678, +31681,31684,31687,31690,31693,31696,31700,31703,31706,31709,31712,31715,31718,31721,31724,31727,31730,31733,31736,31739,31742,31745,31748,31751,31754,31757,31760,31763,31766,31768,31771,31774,31777,31780,31783,31786,31789,31792,31795,31798,31801,31804,31806,31809,31812,31815,31818,31821,31824,31827, +31830,31832,31835,31838,31841,31844,31847,31849,31852,31855,31858,31861,31864,31866,31869,31872,31875,31878,31880,31883,31886,31889,31891,31894,31897,31900,31902,31905,31908,31911,31913,31916,31919,31922,31924,31927,31930,31933,31935,31938,31941,31943,31946,31949,31951,31954,31957,31959,31962,31965, +31967,31970,31973,31975,31978,31980,31983,31986,31988,31991,31994,31996,31999,32001,32004,32007,32009,32012,32014,32017,32019,32022,32025,32027,32030,32032,32035,32037,32040,32042,32045,32047,32050,32052,32055,32057,32060,32062,32065,32067,32070,32072,32075,32077,32080,32082,32085,32087,32090,32092, +32094,32097,32099,32102,32104,32107,32109,32111,32114,32116,32119,32121,32123,32126,32128,32130,32133,32135,32138,32140,32142,32145,32147,32149,32152,32154,32156,32159,32161,32163,32166,32168,32170,32172,32175,32177,32179,32182,32184,32186,32188,32191,32193,32195,32197,32200,32202,32204,32206,32209, +32211,32213,32215,32217,32220,32222,32224,32226,32228,32231,32233,32235,32237,32239,32241,32244,32246,32248,32250,32252,32254,32256,32259,32261,32263,32265,32267,32269,32271,32273,32275,32277,32280,32282,32284,32286,32288,32290,32292,32294,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314, +32316,32318,32320,32322,32324,32326,32328,32330,32332,32334,32336,32338,32340,32342,32344,32346,32348,32350,32352,32353,32355,32357,32359,32361,32363,32365,32367,32369,32371,32372,32374,32376,32378,32380,32382,32384,32385,32387,32389,32391,32393,32395,32396,32398,32400,32402,32404,32405,32407,32409, +32411,32413,32414,32416,32418,32420,32421,32423,32425,32427,32428,32430,32432,32434,32435,32437,32439,32440,32442,32444,32446,32447,32449,32451,32452,32454,32456,32457,32459,32461,32462,32464,32466,32467,32469,32470,32472,32474,32475,32477,32479,32480,32482,32483,32485,32487,32488,32490,32491,32493, +32494,32496,32497,32499,32501,32502,32504,32505,32507,32508,32510,32511,32513,32514,32516,32517,32519,32520,32522,32523,32525,32526,32528,32529,32531,32532,32533,32535,32536,32538,32539,32541,32542,32544,32545,32546,32548,32549,32551,32552,32553,32555,32556,32557,32559,32560,32562,32563,32564,32566, +32567,32568,32570,32571,32572,32574,32575,32576,32578,32579,32580,32581,32583,32584,32585,32587,32588,32589,32590,32592,32593,32594,32595,32597,32598,32599,32600,32601,32603,32604,32605,32606,32608,32609,32610,32611,32612,32613,32615,32616,32617,32618,32619,32620,32622,32623,32624,32625,32626,32627, +32628,32629,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32670,32671,32672,32673,32674,32675,32676,32677,32678, +32679,32680,32680,32681,32682,32683,32684,32685,32686,32687,32687,32688,32689,32690,32691,32692,32692,32693,32694,32695,32696,32696,32697,32698,32699,32700,32700,32701,32702,32703,32704,32704,32705,32706,32707,32707,32708,32709,32709,32710,32711,32712,32712,32713,32714,32714,32715,32716,32716,32717, +32718,32718,32719,32720,32720,32721,32722,32722,32723,32724,32724,32725,32726,32726,32727,32727,32728,32729,32729,32730,32730,32731,32731,32732,32733,32733,32734,32734,32735,32735,32736,32736,32737,32738,32738,32739,32739,32740,32740,32741,32741,32742,32742,32743,32743,32743,32744,32744,32745,32745, +32746,32746,32747,32747,32748,32748,32748,32749,32749,32750,32750,32750,32751,32751,32752,32752,32752,32753,32753,32753,32754,32754,32755,32755,32755,32756,32756,32756,32757,32757,32757,32757,32758,32758,32758,32759,32759,32759,32760,32760,32760,32760,32761,32761,32761,32761,32762,32762,32762,32762, +32762,32763,32763,32763,32763,32764,32764,32764,32764,32764,32764,32765,32765,32765,32765,32765,32765,32766,32766,32766,32766,32766,32766,32766,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768, +32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32766,32766,32766,32766,32766,32766,32766,32765,32765,32765,32765,32765,32765,32764,32764,32764,32764,32764,32764,32763,32763,32763,32763,32762, +32762,32762,32762,32762,32761,32761,32761,32761,32760,32760,32760,32760,32759,32759,32759,32758,32758,32758,32757,32757,32757,32757,32756,32756,32756,32755,32755,32755,32754,32754,32753,32753,32753,32752,32752,32752,32751,32751,32750,32750,32750,32749,32749,32748,32748,32748,32747,32747,32746,32746, +32745,32745,32744,32744,32743,32743,32743,32742,32742,32741,32741,32740,32740,32739,32739,32738,32738,32737,32736,32736,32735,32735,32734,32734,32733,32733,32732,32731,32731,32730,32730,32729,32729,32728,32727,32727,32726,32726,32725,32724,32724,32723,32722,32722,32721,32720,32720,32719,32718,32718, +32717,32716,32716,32715,32714,32714,32713,32712,32712,32711,32710,32709,32709,32708,32707,32707,32706,32705,32704,32704,32703,32702,32701,32700,32700,32699,32698,32697,32696,32696,32695,32694,32693,32692,32692,32691,32690,32689,32688,32687,32687,32686,32685,32684,32683,32682,32681,32680,32680,32679, +32678,32677,32676,32675,32674,32673,32672,32671,32670,32670,32669,32668,32667,32666,32665,32664,32663,32662,32661,32660,32659,32658,32657,32656,32655,32654,32653,32652,32651,32650,32649,32648,32647,32646,32645,32644,32643,32641,32640,32639,32638,32637,32636,32635,32634,32633,32632,32631,32629,32628, +32627,32626,32625,32624,32623,32622,32620,32619,32618,32617,32616,32615,32613,32612,32611,32610,32609,32608,32606,32605,32604,32603,32601,32600,32599,32598,32597,32595,32594,32593,32592,32590,32589,32588,32587,32585,32584,32583,32581,32580,32579,32578,32576,32575,32574,32572,32571,32570,32568,32567, +32566,32564,32563,32562,32560,32559,32557,32556,32555,32553,32552,32551,32549,32548,32546,32545,32544,32542,32541,32539,32538,32536,32535,32533,32532,32531,32529,32528,32526,32525,32523,32522,32520,32519,32517,32516,32514,32513,32511,32510,32508,32507,32505,32504,32502,32501,32499,32497,32496,32494, +32493,32491,32490,32488,32487,32485,32483,32482,32480,32479,32477,32475,32474,32472,32470,32469,32467,32466,32464,32462,32461,32459,32457,32456,32454,32452,32451,32449,32447,32446,32444,32442,32440,32439,32437,32435,32434,32432,32430,32428,32427,32425,32423,32421,32420,32418,32416,32414,32413,32411, +32409,32407,32405,32404,32402,32400,32398,32396,32395,32393,32391,32389,32387,32385,32384,32382,32380,32378,32376,32374,32372,32371,32369,32367,32365,32363,32361,32359,32357,32355,32353,32352,32350,32348,32346,32344,32342,32340,32338,32336,32334,32332,32330,32328,32326,32324,32322,32320,32318,32316, +32314,32312,32310,32308,32306,32304,32302,32300,32298,32296,32294,32292,32290,32288,32286,32284,32282,32280,32277,32275,32273,32271,32269,32267,32265,32263,32261,32259,32256,32254,32252,32250,32248,32246,32244,32241,32239,32237,32235,32233,32231,32228,32226,32224,32222,32220,32217,32215,32213,32211, +32209,32206,32204,32202,32200,32197,32195,32193,32191,32188,32186,32184,32182,32179,32177,32175,32172,32170,32168,32166,32163,32161,32159,32156,32154,32152,32149,32147,32145,32142,32140,32138,32135,32133,32130,32128,32126,32123,32121,32119,32116,32114,32111,32109,32107,32104,32102,32099,32097,32094, +32092,32090,32087,32085,32082,32080,32077,32075,32072,32070,32067,32065,32062,32060,32057,32055,32052,32050,32047,32045,32042,32040,32037,32035,32032,32030,32027,32025,32022,32019,32017,32014,32012,32009,32007,32004,32001,31999,31996,31994,31991,31988,31986,31983,31980,31978,31975,31973,31970,31967, +31965,31962,31959,31957,31954,31951,31949,31946,31943,31941,31938,31935,31933,31930,31927,31924,31922,31919,31916,31913,31911,31908,31905,31902,31900,31897,31894,31891,31889,31886,31883,31880,31878,31875,31872,31869,31866,31864,31861,31858,31855,31852,31849,31847,31844,31841,31838,31835,31832,31830, +31827,31824,31821,31818,31815,31812,31809,31806,31804,31801,31798,31795,31792,31789,31786,31783,31780,31777,31774,31771,31768,31766,31763,31760,31757,31754,31751,31748,31745,31742,31739,31736,31733,31730,31727,31724,31721,31718,31715,31712,31709,31706,31703,31700,31696,31693,31690,31687,31684,31681, +31678,31675,31672,31669,31666,31663,31660,31656,31653,31650,31647,31644,31641,31638,31635,31631,31628,31625,31622,31619,31616,31613,31609,31606,31603,31600,31597,31593,31590,31587,31584,31581,31577,31574,31571,31568,31565,31561,31558,31555,31552,31548,31545,31542,31539,31535,31532,31529,31526,31522, +31519,31516,31512,31509,31506,31503,31499,31496,31493,31489,31486,31483,31479,31476,31473,31469,31466,31463,31459,31456,31452,31449,31446,31442,31439,31436,31432,31429,31425,31422,31419,31415,31412,31408,31405,31401,31398,31395,31391,31388,31384,31381,31377,31374,31370,31367,31363,31360,31357,31353, +31350,31346,31343,31339,31336,31332,31329,31325,31321,31318,31314,31311,31307,31304,31300,31297,31293,31290,31286,31282,31279,31275,31272,31268,31265,31261,31257,31254,31250,31247,31243,31239,31236,31232,31228,31225,31221,31218,31214,31210,31207,31203,31199,31196,31192,31188,31185,31181,31177,31174, +31170,31166,31162,31159,31155,31151,31148,31144,31140,31136,31133,31129,31125,31121,31118,31114,31110,31106,31103,31099,31095,31091,31088,31084,31080,31076,31072,31069,31065,31061,31057,31053,31050,31046,31042,31038,31034,31030,31026,31023,31019,31015,31011,31007,31003,30999,30996,30992,30988,30984, +30980,30976,30972,30968,30964,30960,30957,30953,30949,30945,30941,30937,30933,30929,30925,30921,30917,30913,30909,30905,30901,30897,30893,30889,30885,30881,30877,30873,30869,30865,30861,30857,30853,30849,30845,30841,30837,30833,30829,30825,30821,30817,30813,30809,30805,30801,30797,30792,30788,30784, +30780,30776,30772,30768,30764,30760,30756,30751,30747,30743,30739,30735,30731,30727,30722,30718,30714,30710,30706,30702,30697,30693,30689,30685,30681,30677,30672,30668,30664,30660,30656,30651,30647,30643,30639,30634,30630,30626,30622,30617,30613,30609,30605,30600,30596,30592,30588,30583,30579,30575, +30570,30566,30562,30558,30553,30549,30545,30540,30536,30532,30527,30523,30519,30514,30510,30506,30501,30497,30493,30488,30484,30479,30475,30471,30466,30462,30458,30453,30449,30444,30440,30436,30431,30427,30422,30418,30413,30409,30405,30400,30396,30391,30387,30382,30378,30373,30369,30364,30360,30356, +30351,30347,30342,30338,30333,30329,30324,30320,30315,30311,30306,30301,30297,30292,30288,30283,30279,30274,30270,30265,30261,30256,30251,30247,30242,30238,30233,30229,30224,30219,30215,30210,30206,30201,30196,30192,30187,30182,30178,30173,30169,30164,30159,30155,30150,30145,30141,30136,30131,30127, +30122,30117,30113,30108,30103,30099,30094,30089,30084,30080,30075,30070,30066,30061,30056,30051,30047,30042,30037,30033,30028,30023,30018,30013,30009,30004,29999,29994,29990,29985,29980,29975,29970,29966,29961,29956,29951,29946,29942,29937,29932,29927,29922,29918,29913,29908,29903,29898,29893,29888, +29884,29879,29874,29869,29864,29859,29854,29849,29845,29840,29835,29830,29825,29820,29815,29810,29805,29800,29795,29791,29786,29781,29776,29771,29766,29761,29756,29751,29746,29741,29736,29731,29726,29721,29716,29711,29706,29701,29696,29691,29686,29681,29676,29671,29666,29661,29656,29651,29646,29641, +29636,29631,29626,29621,29616,29611,29606,29601,29595,29590,29585,29580,29575,29570,29565,29560,29555,29550,29545,29539,29534,29529,29524,29519,29514,29509,29504,29498,29493,29488,29483,29478,29473,29467,29462,29457,29452,29447,29442,29436,29431,29426,29421,29416,29410,29405,29400,29395,29390,29384, +29379,29374,29369,29363,29358,29353,29348,29342,29337,29332,29327,29321,29316,29311,29306,29300,29295,29290,29285,29279,29274,29269,29263,29258,29253,29247,29242,29237,29231,29226,29221,29215,29210,29205,29199,29194,29189,29183,29178,29173,29167,29162,29157,29151,29146,29140,29135,29130,29124,29119, +29113,29108,29103,29097,29092,29086,29081,29076,29070,29065,29059,29054,29048,29043,29038,29032,29027,29021,29016,29010,29005,28999,28994,28988,28983,28977,28972,28966,28961,28955,28950,28944,28939,28933,28928,28922,28917,28911,28906,28900,28895,28889,28884,28878,28872,28867,28861,28856,28850,28845, +28839,28834,28828,28822,28817,28811,28806,28800,28794,28789,28783,28778,28772,28766,28761,28755,28749,28744,28738,28733,28727,28721,28716,28710,28704,28699,28693,28687,28682,28676,28670,28665,28659,28653,28648,28642,28636,28631,28625,28619,28613,28608,28602,28596,28591,28585,28579,28573,28568,28562, +28556,28550,28545,28539,28533,28527,28522,28516,28510,28504,28499,28493,28487,28481,28475,28470,28464,28458,28452,28446,28441,28435,28429,28423,28417,28412,28406,28400,28394,28388,28382,28377,28371,28365,28359,28353,28347,28341,28336,28330,28324,28318,28312,28306,28300,28294,28288,28283,28277,28271, +28265,28259,28253,28247,28241,28235,28229,28223,28218,28212,28206,28200,28194,28188,28182,28176,28170,28164,28158,28152,28146,28140,28134,28128,28122,28116,28110,28104,28098,28092,28086,28080,28074,28068,28062,28056,28050,28044,28038,28032,28026,28020,28014,28008,28002,27996,27990,27984,27978,27972, +27965,27959,27953,27947,27941,27935,27929,27923,27917,27911,27905,27898,27892,27886,27880,27874,27868,27862,27856,27850,27843,27837,27831,27825,27819,27813,27807,27800,27794,27788,27782,27776,27770,27763,27757,27751,27745,27739,27733,27726,27720,27714,27708,27702,27695,27689,27683,27677,27671,27664, +27658,27652,27646,27639,27633,27627,27621,27614,27608,27602,27596,27589,27583,27577,27571,27564,27558,27552,27546,27539,27533,27527,27520,27514,27508,27501,27495,27489,27483,27476,27470,27464,27457,27451,27445,27438,27432,27426,27419,27413,27407,27400,27394,27388,27381,27375,27368,27362,27356,27349, +27343,27337,27330,27324,27317,27311,27305,27298,27292,27285,27279,27273,27266,27260,27253,27247,27241,27234,27228,27221,27215,27208,27202,27196,27189,27183,27176,27170,27163,27157,27150,27144,27137,27131,27124,27118,27111,27105,27098,27092,27086,27079,27073,27066,27059,27053,27046,27040,27033,27027, +27020,27014,27007,27001,26994,26988,26981,26975,26968,26962,26955,26948,26942,26935,26929,26922,26916,26909,26902,26896,26889,26883,26876,26870,26863,26856,26850,26843,26837,26830,26823,26817,26810,26803,26797,26790,26784,26777,26770,26764,26757,26750,26744,26737,26730,26724,26717,26711,26704,26697, +26691,26684,26677,26670,26664,26657,26650,26644,26637,26630,26624,26617,26610,26604,26597,26590,26583,26577,26570,26563,26557,26550,26543,26536,26530,26523,26516,26509,26503,26496,26489,26482,26476,26469,26462,26455,26449,26442,26435,26428,26421,26415,26408,26401,26394,26388,26381,26374,26367,26360, +26354,26347,26340,26333,26326,26319,26313,26306,26299,26292,26285,26278,26272,26265,26258,26251,26244,26237,26231,26224,26217,26210,26203,26196,26189,26182,26176,26169,26162,26155,26148,26141,26134,26127,26121,26114,26107,26100,26093,26086,26079,26072,26065,26058,26051,26044,26038,26031,26024,26017, +26010,26003,25996,25989,25982,25975,25968,25961,25954,25947,25940,25933,25926,25919,25912,25905,25898,25891,25884,25877,25870,25863,25856,25849,25842,25835,25828,25821,25814,25807,25800,25793,25786,25779,25772,25765,25758,25751,25744,25737,25730,25723,25716,25709,25702,25695,25688,25681,25674,25666, +25659,25652,25645,25638,25631,25624,25617,25610,25603,25596,25589,25581,25574,25567,25560,25553,25546,25539,25532,25525,25517,25510,25503,25496,25489,25482,25475,25468,25460,25453,25446,25439,25432,25425,25417,25410,25403,25396,25389,25382,25374,25367,25360,25353,25346,25339,25331,25324,25317,25310, +25303,25295,25288,25281,25274,25267,25259,25252,25245,25238,25231,25223,25216,25209,25202,25194,25187,25180,25173,25165,25158,25151,25144,25136,25129,25122,25115,25107,25100,25093,25086,25078,25071,25064,25057,25049,25042,25035,25027,25020,25013,25006,24998,24991,24984,24976,24969,24962,24954,24947, +24940,24932,24925,24918,24910,24903,24896,24888,24881,24874,24866,24859,24852,24844,24837,24830,24822,24815,24808,24800,24793,24786,24778,24771,24763,24756,24749,24741,24734,24727,24719,24712,24704,24697,24690,24682,24675,24667,24660,24653,24645,24638,24630,24623,24616,24608,24601,24593,24586,24578, +24571,24564,24556,24549,24541,24534,24526,24519,24512,24504,24497,24489,24482,24474,24467,24459,24452,24444,24437,24429,24422,24414,24407,24400,24392,24385,24377,24370,24362,24355,24347,24340,24332,24325,24317,24310,24302,24295,24287,24280,24272,24264,24257,24249,24242,24234,24227,24219,24212,24204, +24197,24189,24182,24174,24167,24159,24151,24144,24136,24129,24121,24114,24106,24098,24091,24083,24076,24068,24061,24053,24045,24038,24030,24023,24015,24008,24000,23992,23985,23977,23970,23962,23954,23947,23939,23931,23924,23916,23909,23901,23893,23886,23878,23870,23863,23855,23848,23840,23832,23825, +23817,23809,23802,23794,23786,23779,23771,23763,23756,23748,23740,23733,23725,23717,23710,23702,23694,23687,23679,23671,23664,23656,23648,23641,23633,23625,23618,23610,23602,23594,23587,23579,23571,23564,23556,23548,23540,23533,23525,23517,23510,23502,23494,23486,23479,23471,23463,23455,23448,23440, +23432,23424,23417,23409,23401,23393,23386,23378,23370,23362,23355,23347,23339,23331,23324,23316,23308,23300,23293,23285,23277,23269,23261,23254,23246,23238,23230,23222,23215,23207,23199,23191,23183,23176,23168,23160,23152,23144,23137,23129,23121,23113,23105,23097,23090,23082,23074,23066,23058,23050, +23043,23035,23027,23019,23011,23003,22996,22988,22980,22972,22964,22956,22948,22941,22933,22925,22917,22909,22901,22893,22885,22878,22870,22862,22854,22846,22838,22830,22822,22815,22807,22799,22791,22783,22775,22767,22759,22751,22743,22736,22728,22720,22712,22704,22696,22688,22680,22672,22664,22656, +22648,22641,22633,22625,22617,22609,22601,22593,22585,22577,22569,22561,22553,22545,22537,22529,22521,22513,22505,22498,22490,22482,22474,22466,22458,22450,22442,22434,22426,22418,22410,22402,22394,22386,22378,22370,22362,22354,22346,22338,22330,22322,22314,22306,22298,22290,22282,22274,22266,22258, +22250,22242,22234,22226,22218,22210,22202,22194,22186,22178,22170,22162,22154,22146,22138,22130,22122,22114,22106,22097,22089,22081,22073,22065,22057,22049,22041,22033,22025,22017,22009,22001,21993,21985,21977,21969,21961,21952,21944,21936,21928,21920,21912,21904,21896,21888,21880,21872,21864,21856, +21847,21839,21831,21823,21815,21807,21799,21791,21783,21775,21767,21758,21750,21742,21734,21726,21718,21710,21702,21694,21685,21677,21669,21661,21653,21645,21637,21629,21620,21612,21604,21596,21588,21580,21572,21563,21555,21547,21539,21531,21523,21515,21506,21498,21490,21482,21474,21466,21458,21449, +21441,21433,21425,21417,21409,21400,21392,21384,21376,21368,21360,21351,21343,21335,21327,21319,21310,21302,21294,21286,21278,21270,21261,21253,21245,21237,21229,21220,21212,21204,21196,21188,21179,21171,21163,21155,21147,21138,21130,21122,21114,21106,21097,21089,21081,21073,21064,21056,21048,21040, +21032,21023,21015,21007,20999,20990,20982,20974,20966,20957,20949,20941,20933,20924,20916,20908,20900,20891,20883,20875,20867,20858,20850,20842,20834,20825,20817,20809,20801,20792,20784,20776,20768,20759,20751,20743,20735,20726,20718,20710,20701,20693,20685,20677,20668,20660,20652,20643,20635,20627, +20619,20610,20602,20594,20585,20577,20569,20561,20552,20544,20536,20527,20519,20511,20502,20494,20486,20478,20469,20461,20453,20444,20436,20428,20419,20411,20403,20394,20386,20378,20369,20361,20353,20345,20336,20328,20320,20311,20303,20295,20286,20278,20270,20261,20253,20245,20236,20228,20220,20211, +20203,20194,20186,20178,20169,20161,20153,20144,20136,20128,20119,20111,20103,20094,20086,20078,20069,20061,20052,20044,20036,20027,20019,20011,20002,19994,19986,19977,19969,19960,19952,19944,19935,19927,19919,19910,19902,19893,19885,19877,19868,19860,19852,19843,19835,19826,19818,19810,19801,19793, +19784,19776,19768,19759,19751,19742,19734,19726,19717,19709,19700,19692,19684,19675,19667,19658,19650,19642,19633,19625,19616,19608,19600,19591,19583,19574,19566,19557,19549,19541,19532,19524,19515,19507,19499,19490,19482,19473,19465,19456,19448,19440,19431,19423,19414,19406,19397,19389,19381,19372, +19364,19355,19347,19338,19330,19321,19313,19305,19296,19288,19279,19271,19262,19254,19245,19237,19229,19220,19212,19203,19195,19186,19178,19169,19161,19153,19144,19136,19127,19119,19110,19102,19093,19085,19076,19068,19059,19051,19043,19034,19026,19017,19009,19000,18992,18983,18975,18966,18958,18949, +18941,18932,18924,18915,18907,18898,18890,18882,18873,18865,18856,18848,18839,18831,18822,18814,18805,18797,18788,18780,18771,18763,18754,18746,18737,18729,18720,18712,18703,18695,18686,18678,18669,18661,18652,18644,18635,18627,18618,18610,18601,18593,18584,18576,18567,18559,18550,18542,18533,18525, +18516,18508,18499,18491,18482,18474,18465,18457,18448,18440,18431,18423,18414,18406,18397,18389,18380,18372,18363,18355,18346,18338,18329,18321,18312,18304,18295,18286,18278,18269,18261,18252,18244,18235,18227,18218,18210,18201,18193,18184,18176,18167,18159,18150,18142,18133,18124,18116,18107,18099, +18090,18082,18073,18065,18056,18048,18039,18031,18022,18014,18005,17996,17988,17979,17971,17962,17954,17945,17937,17928,17920,17911,17903,17894,17885,17877,17868,17860,17851,17843,17834,17826,17817,17809,17800,17791,17783,17774,17766,17757,17749,17740,17732,17723,17714,17706,17697,17689,17680,17672, +17663,17655,17646,17638,17629,17620,17612,17603,17595,17586,17578,17569,17561,17552,17543,17535,17526,17518,17509,17501,17492,17483,17475,17466,17458,17449,17441,17432,17424,17415,17406,17398,17389,17381,17372,17364,17355,17346,17338,17329,17321,17312,17304,17295,17287,17278,17269,17261,17252,17244, +17235,17227,17218,17209,17201,17192,17184,17175,17167,17158,17149,17141,17132,17124,17115,17107,17098,17089,17081,17072,17064,17055,17047,17038,17029,17021,17012,17004,16995,16987,16978,16969,16961,16952,16944,16935,16927,16918,16909,16901,16892,16884,16875,16867,16858,16849,16841,16832,16824,16815, +16806,16798,16789,16781,16772,16764,16755,16746,16738,16729,16721,16712,16704,16695,16686,16678,16669,16661,16652,16644,16635,16626,16618,16609,16601,16592,16583,16575,16566,16558,16549,16541,16532,16523,16515,16506,16498,16489,16481,16472,16463,16455,16446,16438,16429,16420,16412,16403,16395,16386, +16378,16369,16360,16352,16343,16335,16326,16318,16309,16300,16292,16283,16275,16266,16257,16249,16240,16232,16223,16215,16206,16197,16189,16180,16172,16163,16155,16146,16137,16129,16120,16112,16103,16094,16086,16077,16069,16060,16052,16043,16034,16026,16017,16009,16000,15992,15983,15974,15966,15957, +15949,15940,15931,15923,15914,15906,15897,15889,15880,15871,15863,15854,15846,15837,15829,15820,15811,15803,15794,15786,15777,15769,15760,15751,15743,15734,15726,15717,15709,15700,15691,15683,15674,15666,15657,15649,15640,15631,15623,15614,15606,15597,15589,15580,15571,15563,15554,15546,15537,15529, +15520,15511,15503,15494,15486,15477,15469,15460,15451,15443,15434,15426,15417,15409,15400,15392,15383,15374,15366,15357,15349,15340,15332,15323,15314,15306,15297,15289,15280,15272,15263,15255,15246,15237,15229,15220,15212,15203,15195,15186,15178,15169,15160,15152,15143,15135,15126,15118,15109,15101, +15092,15083,15075,15066,15058,15049,15041,15032,15024,15015,15006,14998,14989,14981,14972,14964,14955,14947,14938,14930,14921,14912,14904,14895,14887,14878,14870,14861,14853,14844,14836,14827,14819,14810,14801,14793,14784,14776,14767,14759,14750,14742,14733,14725,14716,14708,14699,14690,14682,14673, +14665,14656,14648,14639,14631,14622,14614,14605,14597,14588,14580,14571,14563,14554,14545,14537,14528,14520,14511,14503,14494,14486,14477,14469,14460,14452,14443,14435,14426,14418,14409,14401,14392,14384,14375,14367,14358,14350,14341,14332,14324,14315,14307,14298,14290,14281,14273,14264,14256,14247, +14239,14230,14222,14213,14205,14196,14188,14179,14171,14162,14154,14145,14137,14128,14120,14111,14103,14094,14086,14077,14069,14060,14052,14043,14035,14026,14018,14009,14001,13992,13984,13976,13967,13959,13950,13942,13933,13925,13916,13908,13899,13891,13882,13874,13865,13857,13848,13840,13831,13823, +13814,13806,13797,13789,13781,13772,13764,13755,13747,13738,13730,13721,13713,13704,13696,13687,13679,13670,13662,13654,13645,13637,13628,13620,13611,13603,13594,13586,13577,13569,13561,13552,13544,13535,13527,13518,13510,13501,13493,13485,13476,13468,13459,13451,13442,13434,13425,13417,13409,13400, +13392,13383,13375,13366,13358,13350,13341,13333,13324,13316,13307,13299,13291,13282,13274,13265,13257,13248,13240,13232,13223,13215,13206,13198,13189,13181,13173,13164,13156,13147,13139,13131,13122,13114,13105,13097,13089,13080,13072,13063,13055,13047,13038,13030,13021,13013,13005,12996,12988,12979, +12971,12963,12954,12946,12937,12929,12921,12912,12904,12896,12887,12879,12870,12862,12854,12845,12837,12828,12820,12812,12803,12795,12787,12778,12770,12762,12753,12745,12736,12728,12720,12711,12703,12695,12686,12678,12670,12661,12653,12644,12636,12628,12619,12611,12603,12594,12586,12578,12569,12561, +12553,12544,12536,12528,12519,12511,12503,12494,12486,12478,12469,12461,12453,12444,12436,12428,12419,12411,12403,12394,12386,12378,12369,12361,12353,12344,12336,12328,12320,12311,12303,12295,12286,12278,12270,12261,12253,12245,12236,12228,12220,12212,12203,12195,12187,12178,12170,12162,12154,12145, +12137,12129,12120,12112,12104,12096,12087,12079,12071,12062,12054,12046,12038,12029,12021,12013,12005,11996,11988,11980,11971,11963,11955,11947,11938,11930,11922,11914,11905,11897,11889,11881,11872,11864,11856,11848,11839,11831,11823,11815,11806,11798,11790,11782,11774,11765,11757,11749,11741,11732, +11724,11716,11708,11699,11691,11683,11675,11667,11658,11650,11642,11634,11626,11617,11609,11601,11593,11585,11576,11568,11560,11552,11544,11535,11527,11519,11511,11503,11494,11486,11478,11470,11462,11453,11445,11437,11429,11421,11413,11404,11396,11388,11380,11372,11364,11355,11347,11339,11331,11323, +11315,11306,11298,11290,11282,11274,11266,11257,11249,11241,11233,11225,11217,11209,11200,11192,11184,11176,11168,11160,11152,11144,11135,11127,11119,11111,11103,11095,11087,11079,11070,11062,11054,11046,11038,11030,11022,11014,11006,10997,10989,10981,10973,10965,10957,10949,10941,10933,10925,10916, +10908,10900,10892,10884,10876,10868,10860,10852,10844,10836,10828,10820,10811,10803,10795,10787,10779,10771,10763,10755,10747,10739,10731,10723,10715,10707,10699,10691,10683,10675,10667,10658,10650,10642,10634,10626,10618,10610,10602,10594,10586,10578,10570,10562,10554,10546,10538,10530,10522,10514, +10506,10498,10490,10482,10474,10466,10458,10450,10442,10434,10426,10418,10410,10402,10394,10386,10378,10370,10362,10354,10346,10338,10330,10322,10314,10306,10298,10290,10282,10274,10267,10259,10251,10243,10235,10227,10219,10211,10203,10195,10187,10179,10171,10163,10155,10147,10139,10131,10124,10116, +10108,10100,10092,10084,10076,10068,10060,10052,10044,10036,10029,10021,10013,10005,9997,9989,9981,9973,9965,9957,9950,9942,9934,9926,9918,9910,9902,9894,9886,9879,9871,9863,9855,9847,9839,9831,9824,9816,9808,9800,9792,9784,9776,9769,9761,9753,9745,9737,9729,9721, +9714,9706,9698,9690,9682,9674,9667,9659,9651,9643,9635,9628,9620,9612,9604,9596,9588,9581,9573,9565,9557,9549,9542,9534,9526,9518,9511,9503,9495,9487,9479,9472,9464,9456,9448,9440,9433,9425,9417,9409,9402,9394,9386,9378,9371,9363,9355,9347,9340,9332, +9324,9316,9309,9301,9293,9285,9278,9270,9262,9255,9247,9239,9231,9224,9216,9208,9201,9193,9185,9177,9170,9162,9154,9147,9139,9131,9124,9116,9108,9100,9093,9085,9077,9070,9062,9054,9047,9039,9031,9024,9016,9008,9001,8993,8985,8978,8970,8962,8955,8947, +8940,8932,8924,8917,8909,8901,8894,8886,8878,8871,8863,8856,8848,8840,8833,8825,8817,8810,8802,8795,8787,8779,8772,8764,8757,8749,8742,8734,8726,8719,8711,8704,8696,8688,8681,8673,8666,8658,8651,8643,8635,8628,8620,8613,8605,8598,8590,8583,8575,8568, +8560,8552,8545,8537,8530,8522,8515,8507,8500,8492,8485,8477,8470,8462,8455,8447,8440,8432,8425,8417,8410,8402,8395,8387,8380,8372,8365,8357,8350,8342,8335,8327,8320,8312,8305,8297,8290,8283,8275,8268,8260,8253,8245,8238,8230,8223,8216,8208,8201,8193, +8186,8178,8171,8164,8156,8149,8141,8134,8126,8119,8112,8104,8097,8089,8082,8075,8067,8060,8052,8045,8038,8030,8023,8016,8008,8001,7993,7986,7979,7971,7964,7957,7949,7942,7935,7927,7920,7913,7905,7898,7891,7883,7876,7869,7861,7854,7847,7839,7832,7825, +7817,7810,7803,7795,7788,7781,7773,7766,7759,7752,7744,7737,7730,7722,7715,7708,7701,7693,7686,7679,7671,7664,7657,7650,7642,7635,7628,7621,7613,7606,7599,7592,7584,7577,7570,7563,7556,7548,7541,7534,7527,7519,7512,7505,7498,7491,7483,7476,7469,7462, +7455,7447,7440,7433,7426,7419,7411,7404,7397,7390,7383,7376,7368,7361,7354,7347,7340,7333,7326,7318,7311,7304,7297,7290,7283,7276,7268,7261,7254,7247,7240,7233,7226,7219,7211,7204,7197,7190,7183,7176,7169,7162,7155,7148,7140,7133,7126,7119,7112,7105, +7098,7091,7084,7077,7070,7063,7056,7049,7042,7035,7027,7020,7013,7006,6999,6992,6985,6978,6971,6964,6957,6950,6943,6936,6929,6922,6915,6908,6901,6894,6887,6880,6873,6866,6859,6852,6845,6838,6831,6824,6817,6810,6803,6796,6790,6783,6776,6769,6762,6755, +6748,6741,6734,6727,6720,6713,6706,6699,6692,6685,6679,6672,6665,6658,6651,6644,6637,6630,6623,6616,6610,6603,6596,6589,6582,6575,6568,6561,6555,6548,6541,6534,6527,6520,6513,6507,6500,6493,6486,6479,6472,6466,6459,6452,6445,6438,6431,6425,6418,6411, +6404,6397,6391,6384,6377,6370,6363,6357,6350,6343,6336,6330,6323,6316,6309,6302,6296,6289,6282,6275,6269,6262,6255,6248,6242,6235,6228,6222,6215,6208,6201,6195,6188,6181,6174,6168,6161,6154,6148,6141,6134,6128,6121,6114,6108,6101,6094,6088,6081,6074, +6067,6061,6054,6048,6041,6034,6028,6021,6014,6008,6001,5994,5988,5981,5974,5968,5961,5955,5948,5941,5935,5928,5922,5915,5908,5902,5895,5889,5882,5875,5869,5862,5856,5849,5843,5836,5829,5823,5816,5810,5803,5797,5790,5784,5777,5770,5764,5757,5751,5744, +5738,5731,5725,5718,5712,5705,5699,5692,5686,5679,5673,5666,5660,5653,5647,5640,5634,5627,5621,5614,5608,5602,5595,5589,5582,5576,5569,5563,5556,5550,5544,5537,5531,5524,5518,5511,5505,5499,5492,5486,5479,5473,5467,5460,5454,5447,5441,5435,5428,5422, +5415,5409,5403,5396,5390,5384,5377,5371,5365,5358,5352,5346,5339,5333,5327,5320,5314,5308,5301,5295,5289,5282,5276,5270,5263,5257,5251,5244,5238,5232,5226,5219,5213,5207,5201,5194,5188,5182,5175,5169,5163,5157,5150,5144,5138,5132,5125,5119,5113,5107, +5101,5094,5088,5082,5076,5070,5063,5057,5051,5045,5039,5032,5026,5020,5014,5008,5001,4995,4989,4983,4977,4971,4964,4958,4952,4946,4940,4934,4928,4921,4915,4909,4903,4897,4891,4885,4879,4873,4866,4860,4854,4848,4842,4836,4830,4824,4818,4812,4806,4800, +4793,4787,4781,4775,4769,4763,4757,4751,4745,4739,4733,4727,4721,4715,4709,4703,4697,4691,4685,4679,4673,4667,4661,4655,4649,4643,4637,4631,4625,4619,4613,4607,4601,4595,4589,4583,4577,4571,4565,4559,4553,4548,4542,4536,4530,4524,4518,4512,4506,4500, +4494,4488,4482,4477,4471,4465,4459,4453,4447,4441,4435,4430,4424,4418,4412,4406,4400,4394,4389,4383,4377,4371,4365,4359,4354,4348,4342,4336,4330,4324,4319,4313,4307,4301,4295,4290,4284,4278,4272,4267,4261,4255,4249,4243,4238,4232,4226,4220,4215,4209, +4203,4198,4192,4186,4180,4175,4169,4163,4157,4152,4146,4140,4135,4129,4123,4118,4112,4106,4101,4095,4089,4083,4078,4072,4067,4061,4055,4050,4044,4038,4033,4027,4021,4016,4010,4004,3999,3993,3988,3982,3976,3971,3965,3960,3954,3948,3943,3937,3932,3926, +3921,3915,3909,3904,3898,3893,3887,3882,3876,3871,3865,3860,3854,3848,3843,3837,3832,3826,3821,3815,3810,3804,3799,3793,3788,3782,3777,3771,3766,3761,3755,3750,3744,3739,3733,3728,3722,3717,3711,3706,3701,3695,3690,3684,3679,3673,3668,3663,3657,3652, +3646,3641,3636,3630,3625,3619,3614,3609,3603,3598,3593,3587,3582,3577,3571,3566,3561,3555,3550,3545,3539,3534,3529,3523,3518,3513,3507,3502,3497,3491,3486,3481,3476,3470,3465,3460,3454,3449,3444,3439,3433,3428,3423,3418,3412,3407,3402,3397,3391,3386, +3381,3376,3371,3365,3360,3355,3350,3345,3339,3334,3329,3324,3319,3313,3308,3303,3298,3293,3288,3282,3277,3272,3267,3262,3257,3252,3247,3241,3236,3231,3226,3221,3216,3211,3206,3201,3195,3190,3185,3180,3175,3170,3165,3160,3155,3150,3145,3140,3135,3130, +3125,3119,3114,3109,3104,3099,3094,3089,3084,3079,3074,3069,3064,3059,3054,3049,3044,3039,3034,3029,3024,3020,3015,3010,3005,3000,2995,2990,2985,2980,2975,2970,2965,2960,2955,2950,2945,2941,2936,2931,2926,2921,2916,2911,2906,2901,2897,2892,2887,2882, +2877,2872,2867,2863,2858,2853,2848,2843,2838,2834,2829,2824,2819,2814,2810,2805,2800,2795,2790,2786,2781,2776,2771,2766,2762,2757,2752,2747,2743,2738,2733,2728,2724,2719,2714,2709,2705,2700,2695,2691,2686,2681,2676,2672,2667,2662,2658,2653,2648,2644, +2639,2634,2630,2625,2620,2616,2611,2606,2602,2597,2592,2588,2583,2579,2574,2569,2565,2560,2556,2551,2546,2542,2537,2533,2528,2523,2519,2514,2510,2505,2501,2496,2492,2487,2482,2478,2473,2469,2464,2460,2455,2451,2446,2442,2437,2433,2428,2424,2419,2415, +2410,2406,2401,2397,2392,2388,2383,2379,2375,2370,2366,2361,2357,2352,2348,2343,2339,2335,2330,2326,2321,2317,2313,2308,2304,2299,2295,2291,2286,2282,2278,2273,2269,2265,2260,2256,2251,2247,2243,2238,2234,2230,2226,2221,2217,2213,2208,2204,2200,2195, +2191,2187,2183,2178,2174,2170,2165,2161,2157,2153,2148,2144,2140,2136,2131,2127,2123,2119,2115,2110,2106,2102,2098,2094,2089,2085,2081,2077,2073,2068,2064,2060,2056,2052,2048,2043,2039,2035,2031,2027,2023,2019,2015,2010,2006,2002,1998,1994,1990,1986, +1982,1978,1974,1969,1965,1961,1957,1953,1949,1945,1941,1937,1933,1929,1925,1921,1917,1913,1909,1905,1901,1897,1893,1889,1885,1881,1877,1873,1869,1865,1861,1857,1853,1849,1845,1841,1837,1833,1829,1825,1821,1817,1813,1809,1806,1802,1798,1794,1790,1786, +1782,1778,1774,1770,1767,1763,1759,1755,1751,1747,1743,1740,1736,1732,1728,1724,1720,1717,1713,1709,1705,1701,1698,1694,1690,1686,1682,1679,1675,1671,1667,1663,1660,1656,1652,1648,1645,1641,1637,1633,1630,1626,1622,1619,1615,1611,1607,1604,1600,1596, +1593,1589,1585,1582,1578,1574,1571,1567,1563,1560,1556,1552,1549,1545,1541,1538,1534,1530,1527,1523,1520,1516,1512,1509,1505,1502,1498,1494,1491,1487,1484,1480,1477,1473,1470,1466,1462,1459,1455,1452,1448,1445,1441,1438,1434,1431,1427,1424,1420,1417, +1413,1410,1406,1403,1399,1396,1392,1389,1385,1382,1379,1375,1372,1368,1365,1361,1358,1355,1351,1348,1344,1341,1338,1334,1331,1327,1324,1321,1317,1314,1310,1307,1304,1300,1297,1294,1290,1287,1284,1280,1277,1274,1270,1267,1264,1261,1257,1254,1251,1247, +1244,1241,1238,1234,1231,1228,1224,1221,1218,1215,1211,1208,1205,1202,1199,1195,1192,1189,1186,1183,1179,1176,1173,1170,1167,1163,1160,1157,1154,1151,1148,1144,1141,1138,1135,1132,1129,1126,1122,1119,1116,1113,1110,1107,1104,1101,1098,1095,1091,1088, +1085,1082,1079,1076,1073,1070,1067,1064,1061,1058,1055,1052,1049,1046,1043,1040,1037,1034,1031,1028,1025,1022,1019,1016,1013,1010,1007,1004,1001,998,995,992,989,986,983,980,978,975,972,969,966,963,960,957,954,951,949,946,943,940, +937,934,931,928,926,923,920,917,914,911,909,906,903,900,897,895,892,889,886,883,881,878,875,872,870,867,864,861,859,856,853,850,848,845,842,840,837,834,831,829,826,823,821,818,815,813,810,807,805,802, +799,797,794,791,789,786,784,781,778,776,773,771,768,765,763,760,758,755,752,750,747,745,742,740,737,735,732,729,727,724,722,719,717,714,712,709,707,704,702,699,697,694,692,690,687,685,682,680,677,675, +672,670,668,665,663,660,658,655,653,651,648,646,643,641,639,636,634,632,629,627,625,622,620,618,615,613,611,608,606,604,601,599,597,594,592,590,588,585,583,581,579,576,574,572,570,567,565,563,561,558, +556,554,552,550,547,545,543,541,539,536,534,532,530,528,526,523,521,519,517,515,513,511,508,506,504,502,500,498,496,494,492,490,487,485,483,481,479,477,475,473,471,469,467,465,463,461,459,457,455,453, +451,449,447,445,443,441,439,437,435,433,431,429,427,425,423,421,419,417,415,414,412,410,408,406,404,402,400,398,397,395,393,391,389,387,385,383,382,380,378,376,374,373,371,369,367,365,363,362,360,358, +356,355,353,351,349,347,346,344,342,340,339,337,335,334,332,330,328,327,325,323,322,320,318,317,315,313,312,310,308,307,305,303,302,300,298,297,295,293,292,290,289,287,285,284,282,281,279,278,276,274, +273,271,270,268,267,265,264,262,261,259,257,256,254,253,251,250,248,247,245,244,243,241,240,238,237,235,234,232,231,229,228,227,225,224,222,221,220,218,217,215,214,213,211,210,209,207,206,204,203,202, +200,199,198,196,195,194,192,191,190,189,187,186,185,183,182,181,180,178,177,176,175,173,172,171,170,168,167,166,165,163,162,161,160,159,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140, +139,138,137,136,135,134,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,92,91,90, +89,88,87,86,85,84,84,83,82,81,80,79,78,78,77,76,75,74,74,73,72,71,70,70,69,68,67,66,66,65,64,63,63,62,61,60,60,59,58,57,57,56,55,55,54,53,53,52,51,51, +50,49,49,48,47,47,46,45,45,44,43,43,42,42,41,40,40,39,39,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,28,27,27,26,26,25,25,24,24,23,23,22, +22,22,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6, +5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +#endif \ No newline at end of file From 32a71bfbf6ba7b43705f7236a92785cd5a88db52 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 19 Apr 2020 20:42:57 +0200 Subject: [PATCH 034/119] Use long based algorithm --- FMCTCSSRX.cpp | 159 ++++++++++++++++++++++++-------------------------- FMCTCSSRX.h | 8 +-- 2 files changed, 79 insertions(+), 88 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 9391ceb..1224358 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -22,67 +22,73 @@ const struct CTCSS_TABLE { uint8_t frequency; - q31_t coeff; + long coeff; } CTCSS_TABLE_DATA[] = { - { 67U, 131052}, - { 69U, 131051}, - { 71U, 131049}, - { 74U, 131048}, - { 77U, 131046}, - { 79U, 131044}, - { 82U, 131042}, - { 85U, 131040}, - { 88U, 131037}, - { 91U, 131035}, - { 94U, 131032}, - { 97U, 131030}, - {100U, 131028}, - {103U, 131024}, - {107U, 131021}, - {110U, 131017}, - {114U, 131013}, - {118U, 131009}, - {123U, 131005}, - {127U, 131000}, - {131U, 130994}, - {136U, 130989}, - {141U, 130983}, - {146U, 130977}, - {151U, 130970}, - {156U, 130962}, - {159U, 130958}, - {162U, 130954}, - {165U, 130949}, - {167U, 130946}, - {171U, 130941}, - {173U, 130937}, - {177U, 130931}, - {179U, 130927}, - {183U, 130921}, - {186U, 130917}, - {189U, 130911}, - {192U, 130906}, - {196U, 130899}, - {199U, 130894}, - {203U, 130887}, - {206U, 130881}, - {210U, 130873}, - {218U, 130859}, - {225U, 130844}, - {229U, 130837}, - {233U, 130827}, - {241U, 130810}, - {250U, 130791}, - {254U, 130783}}; + {67, 65527}, +{69, 65526}, +{71, 65525}, +{74, 65524}, +{77, 65523}, +{79, 65522}, +{82, 65521}, +{85, 65520}, +{88, 65519}, +{91, 65518}, +{94, 65517}, +{97, 65516}, +{100, 65514}, +{103, 65513}, +{107, 65511}, +{110, 65509}, +{114, 65507}, +{123, 65503}, +{127, 65501}, +{131, 65498}, +{136, 65495}, +{141, 65492}, +{146, 65489}, +{150, 65487}, +{151, 65486}, +{156, 65482}, +{159, 65480}, +{162, 65478}, +{165, 65476}, +{167, 65474}, +{171, 65472}, +{173, 65470}, +{177, 65467}, +{179, 65465}, +{183, 65462}, +{186, 65460}, +{188, 65458}, +{189, 65457}, +{192, 65454}, +{196, 65451}, +{199, 65448}, +{203, 65445}, +{206, 65442}, +{210, 65438}, +{213, 65435}, +{218, 65431}, +{221, 65428}, +{225, 65424}, +{229, 65420}, +{233, 65416}, +{237, 65412}, +{241, 65407}, +{245, 65403}, +{250, 65398}, +{254, 65393} +}; -const uint8_t CTCSS_TABLE_DATA_LEN = 50U; +const uint8_t CTCSS_TABLE_DATA_LEN = 55U; // 4Hz bandwidth const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : m_coeff(0), -m_threshold(0U), +m_thresholdSquared(0U), m_count(0U), m_q0(0), m_q1(0), @@ -102,7 +108,7 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) if (m_coeff == 0) return 4U; - m_threshold = threshold * threshold; + m_thresholdSquared = (long)(threshold * threshold); return 0U; } @@ -110,39 +116,24 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) { for (unsigned int i = 0U; i < length; i++) { - q31_t q2 = m_q1; + + long sampleLong = (long)samples[i]; + + long q2 = m_q1; m_q1 = m_q0; - - // Q31 multiplication, t2 = m_coeff * m_q1 - q63_t t1 = m_coeff * m_q1; - q31_t t2 = __SSAT(t1 >> 32, 31); - - // m_q0 = m_coeff * m_q1 - q2 + samples[i] - m_q0 = t2 - q2 + samples[i]; + m_q0 = m_coeff * m_q1 - q2 + sampleLong; m_count++; - if (m_count == N) { - // Q31 multiplication, t2 = m_q0 * m_q0 - q63_t t1 = m_q0 * m_q0; - q31_t t2 = __SSAT(t1 >> 32, 31); - - // Q31 multiplication, t4 = m_q0 * m_q0 - q63_t t3 = m_q1 * m_q1; - q31_t t4 = __SSAT(t3 >> 32, 31); - - // Q31 multiplication, t8 = m_q0 * m_q1 * m_coeff - q63_t t5 = m_q0 * m_q1; - q31_t t6 = __SSAT(t5 >> 32, 31); - q63_t t7 = t6 * m_coeff; - q31_t t8 = __SSAT(t7 >> 32, 31); - - // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff - q31_t value = t2 + t4 - t8; - - m_result = value >= m_threshold; - m_count = 0U; - m_q0 = 0; - m_q1 = 0; + if(m_count == N) { + m_count = 0; + if(!m_result) { + long magnitudeSquared = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; + if(magnitudeSquared >= m_thresholdSquared) { + m_result = magnitudeSquared >= m_thresholdSquared; + m_q0 = 0; + m_q1 = 0; + } + } } } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index c7fcef9..5d28a58 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -32,11 +32,11 @@ public: void reset(); private: - q31_t m_coeff; - uint16_t m_threshold; + long m_coeff; + long m_thresholdSquared; uint16_t m_count; - q31_t m_q0; - q31_t m_q1; + long m_q0; + long m_q1; bool m_result; }; From c14ae56e2da62aa186cdecc0606e787d789f9138 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 19 Apr 2020 20:43:50 +0200 Subject: [PATCH 035/119] delete old BS --- CTCSSDecoder.cpp | 46 ------------------- CTCSSDecoder.h | 112 ----------------------------------------------- Goertzel.cpp | 82 ---------------------------------- Goertzel.h | 66 ---------------------------- 4 files changed, 306 deletions(-) delete mode 100644 CTCSSDecoder.cpp delete mode 100644 CTCSSDecoder.h delete mode 100644 Goertzel.cpp delete mode 100644 Goertzel.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp deleted file mode 100644 index 8905b2b..0000000 --- a/CTCSSDecoder.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "CTCSSDecoder.h" - -CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : -m_thresholdSquared(threshold * threshold), -m_hasCTCSS(false) -{ - m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); -} - - -void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) -{ - unsigned int f1MagSquared, f2MagSquared, f3MagSquared; - GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); - - if(result == GR_READY) { - // Goertzel says it has something for us, so checkt it - // make sure the strongest tone is the wanted one and not the neighbouring tone - m_hasCTCSS = f2MagSquared >= m_thresholdSquared - && f2MagSquared > f1MagSquared - && f2MagSquared > f3MagSquared; - } -} - -bool CCTCSSDEcoder::hasCTCSS() -{ - return m_hasCTCSS; -} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h deleted file mode 100644 index cf9e5e4..0000000 --- a/CTCSSDecoder.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(CTCSSDECODER_H) -#define CTCSSDECODER_H - -#include "Globals.h" -#include "Goertzel.h" -#include "HannWindow.h" - -#define N_SAMPLES 12000 - -typedef struct -{ - TGoertzelParameters toneLow, tone, toneHi; -} TCTCSSTone; - - -/* - * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. - * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. - * We need in since intermediate values in goertzel will overflow Q15 - */ -const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; -const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; -const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; -const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; -const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; -const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; -const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; -const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; -const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; -const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; -const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; -const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; -const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; -const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; -const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; -const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; -const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; -const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; -const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; -const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; -const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; -const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; -const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; -const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; -const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; -const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; -const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; -const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; -const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; -const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; -const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; -const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; -const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; -const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; -const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; -const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; -const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; -const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; -const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; -const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; -const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; -const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; -const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; -const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; -const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; -const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; -const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; -const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; -const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; -const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; -const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; -const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; -const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; -const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; -const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; - - - -class CCTCSSDEcoder { -public: - //threshold must be 0.0 to 1.0; - CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); - - void samples(const q15_t* samples, uint8_t length); - bool hasCTCSS(); - -private: - unsigned int m_thresholdSquared; - bool m_hasCTCSS; - CGoertzel* m_goertzel; - -}; - -#endif diff --git a/Goertzel.cpp b/Goertzel.cpp deleted file mode 100644 index 811fd97..0000000 --- a/Goertzel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "Goertzel.h" - -CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : -m_min(0), -m_max(0), -m_processedSamplesCount(0), -m_n(n), -m_window(window),//Window should not be deleted by someone else -m_windowCorr(windowCorr) -{ - m_freqs[0] = f1; - m_freqs[1] = f2; - m_freqs[2] = f3; - - reset(); -} - -void CGoertzel::reset() -{ - ::memset(m_q1s, 0, sizeof(m_q1s)); - ::memset(m_q2s, 0, sizeof(m_q2s)); - m_processedSamplesCount = 0U; - m_max = 0U; - m_min = 0U; -} - -GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) -{ - int scalingFactor = (length / 2) * m_windowCorr; - unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; - GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; - - for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { - - if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; - if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; - - for(uint8_t i = 0; i < 3; i++) { - int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); - m_q2s[i] = m_q1s[i]; - m_q1s[i] = q0; - } - - m_processedSamplesCount++; - //we have collected enough samples, evaluate now, - if(m_processedSamplesCount == m_n) { - if(magnitudesComputed == GR_NOT_READY) { - //however if we already had collected enough samples only keep the magnitudes we computed the first time - int span = m_max - m_min; - for(uint8_t i = 0; i < 3; i++) { - int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic - int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; - - *(magnitudes[i]) = real * real + imag * imag; - } - - magnitudesComputed = GR_READY; - } - reset(); - } - } - - return magnitudesComputed; -} diff --git a/Goertzel.h b/Goertzel.h deleted file mode 100644 index 1b15db9..0000000 --- a/Goertzel.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(GOERTZEL_H) -#define GOERTZEL_H - -#include "Globals.h" - -typedef struct -{ - int sin, cos, coeff; -} TGoertzelParameters; - - -enum GOERTZEL_RESULT : uint8_t -{ - GR_NOT_READY = 0, - GR_READY = 1 -}; - -class CGoertzel { -public: - //f1,f2,f3 are natural frequencies - CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); - - void reset(); - GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); - -private: - TGoertzelParameters m_freqs[3]; - - q15_t m_min; - q15_t m_max; - - q15_t m_omegas[3]; - int m_sines[3]; - int m_cosines[3]; - int m_coeffs[3]; - - int m_q1s[3]; - int m_q2s[3]; - - uint16_t m_processedSamplesCount; - uint16_t m_n; - - const int* m_window; - int m_windowCorr; -}; - -#endif From 116b51bd8d9b156d1dc7513e8b802fe239ecd9f0 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 19 Apr 2020 20:45:14 +0200 Subject: [PATCH 036/119] delete old BS --- CTCSSDecoder.cpp | 46 ------------------- CTCSSDecoder.h | 112 ----------------------------------------------- Goertzel.cpp | 82 ---------------------------------- Goertzel.h | 66 ---------------------------- 4 files changed, 306 deletions(-) delete mode 100644 CTCSSDecoder.cpp delete mode 100644 CTCSSDecoder.h delete mode 100644 Goertzel.cpp delete mode 100644 Goertzel.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp deleted file mode 100644 index 8905b2b..0000000 --- a/CTCSSDecoder.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "CTCSSDecoder.h" - -CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : -m_thresholdSquared(threshold * threshold), -m_hasCTCSS(false) -{ - m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); -} - - -void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) -{ - unsigned int f1MagSquared, f2MagSquared, f3MagSquared; - GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); - - if(result == GR_READY) { - // Goertzel says it has something for us, so checkt it - // make sure the strongest tone is the wanted one and not the neighbouring tone - m_hasCTCSS = f2MagSquared >= m_thresholdSquared - && f2MagSquared > f1MagSquared - && f2MagSquared > f3MagSquared; - } -} - -bool CCTCSSDEcoder::hasCTCSS() -{ - return m_hasCTCSS; -} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h deleted file mode 100644 index cf9e5e4..0000000 --- a/CTCSSDecoder.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(CTCSSDECODER_H) -#define CTCSSDECODER_H - -#include "Globals.h" -#include "Goertzel.h" -#include "HannWindow.h" - -#define N_SAMPLES 12000 - -typedef struct -{ - TGoertzelParameters toneLow, tone, toneHi; -} TCTCSSTone; - - -/* - * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. - * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. - * We need in since intermediate values in goertzel will overflow Q15 - */ -const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; -const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; -const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; -const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; -const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; -const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; -const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; -const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; -const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; -const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; -const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; -const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; -const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; -const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; -const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; -const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; -const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; -const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; -const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; -const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; -const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; -const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; -const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; -const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; -const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; -const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; -const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; -const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; -const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; -const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; -const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; -const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; -const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; -const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; -const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; -const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; -const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; -const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; -const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; -const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; -const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; -const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; -const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; -const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; -const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; -const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; -const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; -const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; -const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; -const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; -const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; -const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; -const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; -const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; -const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; - - - -class CCTCSSDEcoder { -public: - //threshold must be 0.0 to 1.0; - CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); - - void samples(const q15_t* samples, uint8_t length); - bool hasCTCSS(); - -private: - unsigned int m_thresholdSquared; - bool m_hasCTCSS; - CGoertzel* m_goertzel; - -}; - -#endif diff --git a/Goertzel.cpp b/Goertzel.cpp deleted file mode 100644 index 811fd97..0000000 --- a/Goertzel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "Goertzel.h" - -CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : -m_min(0), -m_max(0), -m_processedSamplesCount(0), -m_n(n), -m_window(window),//Window should not be deleted by someone else -m_windowCorr(windowCorr) -{ - m_freqs[0] = f1; - m_freqs[1] = f2; - m_freqs[2] = f3; - - reset(); -} - -void CGoertzel::reset() -{ - ::memset(m_q1s, 0, sizeof(m_q1s)); - ::memset(m_q2s, 0, sizeof(m_q2s)); - m_processedSamplesCount = 0U; - m_max = 0U; - m_min = 0U; -} - -GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) -{ - int scalingFactor = (length / 2) * m_windowCorr; - unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; - GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; - - for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { - - if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; - if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; - - for(uint8_t i = 0; i < 3; i++) { - int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); - m_q2s[i] = m_q1s[i]; - m_q1s[i] = q0; - } - - m_processedSamplesCount++; - //we have collected enough samples, evaluate now, - if(m_processedSamplesCount == m_n) { - if(magnitudesComputed == GR_NOT_READY) { - //however if we already had collected enough samples only keep the magnitudes we computed the first time - int span = m_max - m_min; - for(uint8_t i = 0; i < 3; i++) { - int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic - int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; - - *(magnitudes[i]) = real * real + imag * imag; - } - - magnitudesComputed = GR_READY; - } - reset(); - } - } - - return magnitudesComputed; -} diff --git a/Goertzel.h b/Goertzel.h deleted file mode 100644 index 1b15db9..0000000 --- a/Goertzel.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(GOERTZEL_H) -#define GOERTZEL_H - -#include "Globals.h" - -typedef struct -{ - int sin, cos, coeff; -} TGoertzelParameters; - - -enum GOERTZEL_RESULT : uint8_t -{ - GR_NOT_READY = 0, - GR_READY = 1 -}; - -class CGoertzel { -public: - //f1,f2,f3 are natural frequencies - CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); - - void reset(); - GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); - -private: - TGoertzelParameters m_freqs[3]; - - q15_t m_min; - q15_t m_max; - - q15_t m_omegas[3]; - int m_sines[3]; - int m_cosines[3]; - int m_coeffs[3]; - - int m_q1s[3]; - int m_q2s[3]; - - uint16_t m_processedSamplesCount; - uint16_t m_n; - - const int* m_window; - int m_windowCorr; -}; - -#endif From a310bf05476982947d9adaf1af940b534044f207 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 19:46:44 +0100 Subject: [PATCH 037/119] Adjust the coefficients again. --- FMCTCSSRX.cpp | 105 ++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 9391ceb..5b9d139 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -24,56 +24,61 @@ const struct CTCSS_TABLE { uint8_t frequency; q31_t coeff; } CTCSS_TABLE_DATA[] = { - { 67U, 131052}, - { 69U, 131051}, - { 71U, 131049}, - { 74U, 131048}, - { 77U, 131046}, - { 79U, 131044}, - { 82U, 131042}, - { 85U, 131040}, - { 88U, 131037}, - { 91U, 131035}, - { 94U, 131032}, - { 97U, 131030}, - {100U, 131028}, - {103U, 131024}, - {107U, 131021}, - {110U, 131017}, - {114U, 131013}, - {118U, 131009}, - {123U, 131005}, - {127U, 131000}, - {131U, 130994}, - {136U, 130989}, - {141U, 130983}, - {146U, 130977}, - {151U, 130970}, - {156U, 130962}, - {159U, 130958}, - {162U, 130954}, - {165U, 130949}, - {167U, 130946}, - {171U, 130941}, - {173U, 130937}, - {177U, 130931}, - {179U, 130927}, - {183U, 130921}, - {186U, 130917}, - {189U, 130911}, - {192U, 130906}, - {196U, 130899}, - {199U, 130894}, - {203U, 130887}, - {206U, 130881}, - {210U, 130873}, - {218U, 130859}, - {225U, 130844}, - {229U, 130837}, - {233U, 130827}, - {241U, 130810}, - {250U, 130791}, - {254U, 130783}}; + {67, 65527}, + {69, 65526}, + {71, 65525}, + {74, 65524}, + {77, 65523}, + {79, 65522}, + {82, 65521}, + {85, 65520}, + {88, 65519}, + {91, 65518}, + {94, 65517}, + {97, 65516}, + {100, 65514}, + {103, 65513}, + {107, 65511}, + {110, 65509}, + {114, 65507}, + {123, 65503}, + {127, 65501}, + {131, 65498}, + {136, 65495}, + {141, 65492}, + {146, 65489}, + {150, 65487}, + {151, 65486}, + {156, 65482}, + {159, 65480}, + {162, 65478}, + {165, 65476}, + {167, 65474}, + {171, 65472}, + {173, 65470}, + {177, 65467}, + {179, 65465}, + {183, 65462}, + {186, 65460}, + {188, 65458}, + {189, 65457}, + {192, 65454}, + {196, 65451}, + {199, 65448}, + {203, 65445}, + {206, 65442}, + {210, 65438}, + {213, 65435}, + {218, 65431}, + {221, 65428}, + {225, 65424}, + {229, 65420}, + {233, 65416}, + {237, 65412}, + {241, 65407}, + {245, 65403}, + {250, 65398}, + {254, 65393}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; From 1aef1a39f234eb58977c6171c603bde00e5b3abf Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 21:21:10 +0100 Subject: [PATCH 038/119] Revert the CTCSS decoder to floats. --- FMCTCSSRX.cpp | 162 +++++++++++++++++++++----------------------------- FMCTCSSRX.h | 10 ++-- 2 files changed, 74 insertions(+), 98 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 5b9d139..728cc51 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -21,64 +21,59 @@ #include "FMCTCSSRX.h" const struct CTCSS_TABLE { - uint8_t frequency; - q31_t coeff; + uint8_t frequency; + float32_t coeff; } CTCSS_TABLE_DATA[] = { - {67, 65527}, - {69, 65526}, - {71, 65525}, - {74, 65524}, - {77, 65523}, - {79, 65522}, - {82, 65521}, - {85, 65520}, - {88, 65519}, - {91, 65518}, - {94, 65517}, - {97, 65516}, - {100, 65514}, - {103, 65513}, - {107, 65511}, - {110, 65509}, - {114, 65507}, - {123, 65503}, - {127, 65501}, - {131, 65498}, - {136, 65495}, - {141, 65492}, - {146, 65489}, - {150, 65487}, - {151, 65486}, - {156, 65482}, - {159, 65480}, - {162, 65478}, - {165, 65476}, - {167, 65474}, - {171, 65472}, - {173, 65470}, - {177, 65467}, - {179, 65465}, - {183, 65462}, - {186, 65460}, - {188, 65458}, - {189, 65457}, - {192, 65454}, - {196, 65451}, - {199, 65448}, - {203, 65445}, - {206, 65442}, - {210, 65438}, - {213, 65435}, - {218, 65431}, - {221, 65428}, - {225, 65424}, - {229, 65420}, - {233, 65416}, - {237, 65412}, - {241, 65407}, - {245, 65403}, - {250, 65398}, - {254, 65393}}; + { 67U, 1.999692F}, + { 69U, 1.999671F}, + { 71U, 1.999646F}, + { 74U, 1.999621F}, + { 77U, 1.999594F}, + { 79U, 1.999565F}, + { 82U, 1.999534F}, + { 85U, 1.999500F}, + { 88U, 1.999463F}, + { 91U, 1.999426F}, + { 94U, 1.999384F}, + { 97U, 1.999350F}, + {100U, 1.999315F}, + {103U, 1.999266F}, + {107U, 1.999212F}, + {110U, 1.999157F}, + {114U, 1.999097F}, + {118U, 1.999033F}, + {123U, 1.998963F}, + {127U, 1.998889F}, + {131U, 1.998810F}, + {136U, 1.998723F}, + {141U, 1.998632F}, + {146U, 1.998535F}, + {151U, 1.998429F}, + {156U, 1.998317F}, + {159U, 1.998250F}, + {162U, 1.998197F}, + {165U, 1.998123F}, + {167U, 1.998068F}, + {171U, 1.997989F}, + {173U, 1.997930F}, + {177U, 1.997846F}, + {179U, 1.997782F}, + {183U, 1.997693F}, + {186U, 1.997624F}, + {189U, 1.997529F}, + {192U, 1.997453F}, + {196U, 1.997351F}, + {199U, 1.997273F}, + {203U, 1.997162F}, + {206U, 1.997078F}, + {210U, 1.996958F}, + {218U, 1.996741F}, + {225U, 1.996510F}, + {229U, 1.996404F}, + {233U, 1.996261F}, + {241U, 1.995994F}, + {250U, 1.995708F}, + {254U, 1.995576F}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; @@ -86,11 +81,11 @@ const uint8_t CTCSS_TABLE_DATA_LEN = 50U; const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : -m_coeff(0), -m_threshold(0U), +m_coeff(0.0F), +m_threshold(0.0F), m_count(0U), -m_q0(0), -m_q1(0), +m_q0(0.0F), +m_q1(0.0F), m_result(false) { } @@ -104,50 +99,31 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) } } - if (m_coeff == 0) + if (m_coeff == 0.0F) return 4U; - m_threshold = threshold * threshold; + m_threshold = float32_t(threshold * threshold); return 0U; } -bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) +bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) { + float32_t data[RX_BLOCK_SIZE]; + ::arm_q15_to_float(samples, data, length); + for (unsigned int i = 0U; i < length; i++) { - q31_t q2 = m_q1; + float32_t q2 = m_q1; m_q1 = m_q0; - - // Q31 multiplication, t2 = m_coeff * m_q1 - q63_t t1 = m_coeff * m_q1; - q31_t t2 = __SSAT(t1 >> 32, 31); - - // m_q0 = m_coeff * m_q1 - q2 + samples[i] - m_q0 = t2 - q2 + samples[i]; + m_q0 = m_coeff * m_q1 - q2 + data[i]; m_count++; if (m_count == N) { - // Q31 multiplication, t2 = m_q0 * m_q0 - q63_t t1 = m_q0 * m_q0; - q31_t t2 = __SSAT(t1 >> 32, 31); - - // Q31 multiplication, t4 = m_q0 * m_q0 - q63_t t3 = m_q1 * m_q1; - q31_t t4 = __SSAT(t3 >> 32, 31); - - // Q31 multiplication, t8 = m_q0 * m_q1 * m_coeff - q63_t t5 = m_q0 * m_q1; - q31_t t6 = __SSAT(t5 >> 32, 31); - q63_t t7 = t6 * m_coeff; - q31_t t8 = __SSAT(t7 >> 32, 31); - - // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff - q31_t value = t2 + t4 - t8; - + float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; m_result = value >= m_threshold; m_count = 0U; - m_q0 = 0; - m_q1 = 0; + m_q0 = 0.0F; + m_q1 = 0.0F; } } @@ -156,8 +132,8 @@ bool CFMCTCSSRX::process(const q15_t* samples, uint8_t length) void CFMCTCSSRX::reset() { - m_q0 = 0; - m_q1 = 0; + m_q0 = 0.0F; + m_q1 = 0.0F; m_result = false; m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index c7fcef9..b1d7c02 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -27,16 +27,16 @@ public: uint8_t setParams(uint8_t frequency, uint8_t threshold); - bool process(const q15_t* samples, uint8_t length); + bool process(q15_t* samples, uint8_t length); void reset(); private: - q31_t m_coeff; - uint16_t m_threshold; + float32_t m_coeff; + float32_t m_threshold; uint16_t m_count; - q31_t m_q0; - q31_t m_q1; + float32_t m_q0; + float32_t m_q1; bool m_result; }; From 5ed8eeda6fac0b9d143c5f5c62b90ab555ecf4c8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 19 Apr 2020 21:40:51 +0100 Subject: [PATCH 039/119] Allow kerchunk audio to pass through. --- FM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 4528d90..ee060b1 100644 --- a/FM.cpp +++ b/FM.cpp @@ -75,7 +75,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) return; // Only let audio through when relaying audio - if (m_state != FS_RELAYING) { + if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { for (uint8_t i = 0U; i < length; i++) samples[i] = 0; } From b03fead62df33ada7ef8be9b89b3453b1cb0db6e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 13 Apr 2020 16:15:24 +0200 Subject: [PATCH 040/119] Add Goertzel --- Goertzel.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Goertzel.h | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Goertzel.cpp create mode 100644 Goertzel.h diff --git a/Goertzel.cpp b/Goertzel.cpp new file mode 100644 index 0000000..811fd97 --- /dev/null +++ b/Goertzel.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "Goertzel.h" + +CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : +m_min(0), +m_max(0), +m_processedSamplesCount(0), +m_n(n), +m_window(window),//Window should not be deleted by someone else +m_windowCorr(windowCorr) +{ + m_freqs[0] = f1; + m_freqs[1] = f2; + m_freqs[2] = f3; + + reset(); +} + +void CGoertzel::reset() +{ + ::memset(m_q1s, 0, sizeof(m_q1s)); + ::memset(m_q2s, 0, sizeof(m_q2s)); + m_processedSamplesCount = 0U; + m_max = 0U; + m_min = 0U; +} + +GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) +{ + int scalingFactor = (length / 2) * m_windowCorr; + unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; + GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; + + for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { + + if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; + if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; + + for(uint8_t i = 0; i < 3; i++) { + int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); + m_q2s[i] = m_q1s[i]; + m_q1s[i] = q0; + } + + m_processedSamplesCount++; + //we have collected enough samples, evaluate now, + if(m_processedSamplesCount == m_n) { + if(magnitudesComputed == GR_NOT_READY) { + //however if we already had collected enough samples only keep the magnitudes we computed the first time + int span = m_max - m_min; + for(uint8_t i = 0; i < 3; i++) { + int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic + int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; + + *(magnitudes[i]) = real * real + imag * imag; + } + + magnitudesComputed = GR_READY; + } + reset(); + } + } + + return magnitudesComputed; +} diff --git a/Goertzel.h b/Goertzel.h new file mode 100644 index 0000000..1b15db9 --- /dev/null +++ b/Goertzel.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(GOERTZEL_H) +#define GOERTZEL_H + +#include "Globals.h" + +typedef struct +{ + int sin, cos, coeff; +} TGoertzelParameters; + + +enum GOERTZEL_RESULT : uint8_t +{ + GR_NOT_READY = 0, + GR_READY = 1 +}; + +class CGoertzel { +public: + //f1,f2,f3 are natural frequencies + CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); + + void reset(); + GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); + +private: + TGoertzelParameters m_freqs[3]; + + q15_t m_min; + q15_t m_max; + + q15_t m_omegas[3]; + int m_sines[3]; + int m_cosines[3]; + int m_coeffs[3]; + + int m_q1s[3]; + int m_q2s[3]; + + uint16_t m_processedSamplesCount; + uint16_t m_n; + + const int* m_window; + int m_windowCorr; +}; + +#endif From 9e552ba1abb2e6ae01cea1687f176b816cf57efa Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 14 Apr 2020 12:16:07 +0200 Subject: [PATCH 041/119] add CTCSS decoder --- CTCSSDecoder.cpp | 46 ++++++++ CTCSSDecoder.h | 112 ++++++++++++++++++++ HannWindow.h | 268 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 CTCSSDecoder.cpp create mode 100644 CTCSSDecoder.h create mode 100644 HannWindow.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp new file mode 100644 index 0000000..8905b2b --- /dev/null +++ b/CTCSSDecoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "CTCSSDecoder.h" + +CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : +m_thresholdSquared(threshold * threshold), +m_hasCTCSS(false) +{ + m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); +} + + +void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) +{ + unsigned int f1MagSquared, f2MagSquared, f3MagSquared; + GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); + + if(result == GR_READY) { + // Goertzel says it has something for us, so checkt it + // make sure the strongest tone is the wanted one and not the neighbouring tone + m_hasCTCSS = f2MagSquared >= m_thresholdSquared + && f2MagSquared > f1MagSquared + && f2MagSquared > f3MagSquared; + } +} + +bool CCTCSSDEcoder::hasCTCSS() +{ + return m_hasCTCSS; +} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h new file mode 100644 index 0000000..cf9e5e4 --- /dev/null +++ b/CTCSSDecoder.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(CTCSSDECODER_H) +#define CTCSSDECODER_H + +#include "Globals.h" +#include "Goertzel.h" +#include "HannWindow.h" + +#define N_SAMPLES 12000 + +typedef struct +{ + TGoertzelParameters toneLow, tone, toneHi; +} TCTCSSTone; + + +/* + * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. + * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. + * We need in since intermediate values in goertzel will overflow Q15 + */ +const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; +const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; +const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; +const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; +const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; +const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; +const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; +const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; +const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; +const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; +const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; +const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; +const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; +const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; +const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; +const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; +const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; +const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; +const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; +const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; +const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; +const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; +const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; +const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; +const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; +const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; +const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; +const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; +const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; +const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; +const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; +const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; +const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; +const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; +const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; +const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; +const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; +const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; +const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; +const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; +const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; +const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; +const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; +const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; +const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; +const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; +const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; +const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; +const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; +const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; +const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; +const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; +const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; +const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; +const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; + + + +class CCTCSSDEcoder { +public: + //threshold must be 0.0 to 1.0; + CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); + + void samples(const q15_t* samples, uint8_t length); + bool hasCTCSS(); + +private: + unsigned int m_thresholdSquared; + bool m_hasCTCSS; + CGoertzel* m_goertzel; + +}; + +#endif diff --git a/HannWindow.h b/HannWindow.h new file mode 100644 index 0000000..4df9a5e --- /dev/null +++ b/HannWindow.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(HANNWINDOW_H) +#define HANNWINDOW_H + +/* Hann window value as Q15 yet stored into int to avoid overflowing during calculation */ + +const int HANN_WINDOW_CORR = 16383; + +const int HANN_WINDOW[12000] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5, +6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22, +22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,39,39,40,40,41,42,42,43,43,44,45,45,46,47,47,48,49,49,50, +51,51,52,53,53,54,55,55,56,57,57,58,59,60,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,74,74,75,76,77,78,78,79,80,81,82,83,84,84,85,86,87,88,89, +90,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139, +140,141,142,144,145,146,147,148,149,150,152,153,154,155,156,157,159,160,161,162,163,165,166,167,168,170,171,172,173,175,176,177,178,180,181,182,183,185,186,187,189,190,191,192,194,195,196,198,199,200, +202,203,204,206,207,209,210,211,213,214,215,217,218,220,221,222,224,225,227,228,229,231,232,234,235,237,238,240,241,243,244,245,247,248,250,251,253,254,256,257,259,261,262,264,265,267,268,270,271,273, +274,276,278,279,281,282,284,285,287,289,290,292,293,295,297,298,300,302,303,305,307,308,310,312,313,315,317,318,320,322,323,325,327,328,330,332,334,335,337,339,340,342,344,346,347,349,351,353,355,356, +358,360,362,363,365,367,369,371,373,374,376,378,380,382,383,385,387,389,391,393,395,397,398,400,402,404,406,408,410,412,414,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451, +453,455,457,459,461,463,465,467,469,471,473,475,477,479,481,483,485,487,490,492,494,496,498,500,502,504,506,508,511,513,515,517,519,521,523,526,528,530,532,534,536,539,541,543,545,547,550,552,554,556, +558,561,563,565,567,570,572,574,576,579,581,583,585,588,590,592,594,597,599,601,604,606,608,611,613,615,618,620,622,625,627,629,632,634,636,639,641,643,646,648,651,653,655,658,660,663,665,668,670,672, +675,677,680,682,685,687,690,692,694,697,699,702,704,707,709,712,714,717,719,722,724,727,729,732,735,737,740,742,745,747,750,752,755,758,760,763,765,768,771,773,776,778,781,784,786,789,791,794,797,799, +802,805,807,810,813,815,818,821,823,826,829,831,834,837,840,842,845,848,850,853,856,859,861,864,867,870,872,875,878,881,883,886,889,892,895,897,900,903,906,909,911,914,917,920,923,926,928,931,934,937, +940,943,946,949,951,954,957,960,963,966,969,972,975,978,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085, +1088,1091,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1126,1129,1132,1135,1138,1141,1144,1148,1151,1154,1157,1160,1163,1167,1170,1173,1176,1179,1183,1186,1189,1192,1195,1199,1202,1205,1208,1211,1215,1218,1221,1224,1228,1231,1234,1238,1241,1244, +1247,1251,1254,1257,1261,1264,1267,1270,1274,1277,1280,1284,1287,1290,1294,1297,1300,1304,1307,1310,1314,1317,1321,1324,1327,1331,1334,1338,1341,1344,1348,1351,1355,1358,1361,1365,1368,1372,1375,1379,1382,1385,1389,1392,1396,1399,1403,1406,1410,1413, +1417,1420,1424,1427,1431,1434,1438,1441,1445,1448,1452,1455,1459,1462,1466,1470,1473,1477,1480,1484,1487,1491,1494,1498,1502,1505,1509,1512,1516,1520,1523,1527,1530,1534,1538,1541,1545,1549,1552,1556,1560,1563,1567,1571,1574,1578,1582,1585,1589,1593, +1596,1600,1604,1607,1611,1615,1619,1622,1626,1630,1633,1637,1641,1645,1648,1652,1656,1660,1663,1667,1671,1675,1679,1682,1686,1690,1694,1698,1701,1705,1709,1713,1717,1720,1724,1728,1732,1736,1740,1743,1747,1751,1755,1759,1763,1767,1770,1774,1778,1782, +1786,1790,1794,1798,1802,1806,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893,1897,1901,1905,1909,1913,1917,1921,1925,1929,1933,1937,1941,1945,1949,1953,1957,1961,1965,1969,1974,1978,1982, +1986,1990,1994,1998,2002,2006,2010,2015,2019,2023,2027,2031,2035,2039,2043,2048,2052,2056,2060,2064,2068,2073,2077,2081,2085,2089,2094,2098,2102,2106,2110,2115,2119,2123,2127,2131,2136,2140,2144,2148,2153,2157,2161,2165,2170,2174,2178,2183,2187,2191, +2195,2200,2204,2208,2213,2217,2221,2226,2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2273,2278,2282,2286,2291,2295,2299,2304,2308,2313,2317,2321,2326,2330,2335,2339,2343,2348,2352,2357,2361,2366,2370,2375,2379,2383,2388,2392,2397,2401,2406,2410, +2415,2419,2424,2428,2433,2437,2442,2446,2451,2455,2460,2464,2469,2473,2478,2482,2487,2492,2496,2501,2505,2510,2514,2519,2523,2528,2533,2537,2542,2546,2551,2556,2560,2565,2569,2574,2579,2583,2588,2592,2597,2602,2606,2611,2616,2620,2625,2630,2634,2639, +2644,2648,2653,2658,2662,2667,2672,2676,2681,2686,2691,2695,2700,2705,2709,2714,2719,2724,2728,2733,2738,2743,2747,2752,2757,2762,2766,2771,2776,2781,2786,2790,2795,2800,2805,2810,2814,2819,2824,2829,2834,2838,2843,2848,2853,2858,2863,2867,2872,2877, +2882,2887,2892,2897,2901,2906,2911,2916,2921,2926,2931,2936,2941,2945,2950,2955,2960,2965,2970,2975,2980,2985,2990,2995,3000,3005,3010,3015,3020,3024,3029,3034,3039,3044,3049,3054,3059,3064,3069,3074,3079,3084,3089,3094,3099,3104,3109,3114,3119,3125, +3130,3135,3140,3145,3150,3155,3160,3165,3170,3175,3180,3185,3190,3195,3201,3206,3211,3216,3221,3226,3231,3236,3241,3247,3252,3257,3262,3267,3272,3277,3282,3288,3293,3298,3303,3308,3313,3319,3324,3329,3334,3339,3345,3350,3355,3360,3365,3371,3376,3381, +3386,3391,3397,3402,3407,3412,3418,3423,3428,3433,3439,3444,3449,3454,3460,3465,3470,3476,3481,3486,3491,3497,3502,3507,3513,3518,3523,3529,3534,3539,3545,3550,3555,3561,3566,3571,3577,3582,3587,3593,3598,3603,3609,3614,3619,3625,3630,3636,3641,3646, +3652,3657,3663,3668,3673,3679,3684,3690,3695,3701,3706,3711,3717,3722,3728,3733,3739,3744,3750,3755,3761,3766,3771,3777,3782,3788,3793,3799,3804,3810,3815,3821,3826,3832,3837,3843,3848,3854,3860,3865,3871,3876,3882,3887,3893,3898,3904,3909,3915,3921, +3926,3932,3937,3943,3948,3954,3960,3965,3971,3976,3982,3988,3993,3999,4004,4010,4016,4021,4027,4033,4038,4044,4050,4055,4061,4067,4072,4078,4083,4089,4095,4101,4106,4112,4118,4123,4129,4135,4140,4146,4152,4157,4163,4169,4175,4180,4186,4192,4198,4203, +4209,4215,4220,4226,4232,4238,4243,4249,4255,4261,4267,4272,4278,4284,4290,4295,4301,4307,4313,4319,4324,4330,4336,4342,4348,4354,4359,4365,4371,4377,4383,4389,4394,4400,4406,4412,4418,4424,4430,4435,4441,4447,4453,4459,4465,4471,4477,4482,4488,4494, +4500,4506,4512,4518,4524,4530,4536,4542,4548,4553,4559,4565,4571,4577,4583,4589,4595,4601,4607,4613,4619,4625,4631,4637,4643,4649,4655,4661,4667,4673,4679,4685,4691,4697,4703,4709,4715,4721,4727,4733,4739,4745,4751,4757,4763,4769,4775,4781,4787,4793, +4800,4806,4812,4818,4824,4830,4836,4842,4848,4854,4860,4866,4873,4879,4885,4891,4897,4903,4909,4915,4921,4928,4934,4940,4946,4952,4958,4964,4971,4977,4983,4989,4995,5001,5008,5014,5020,5026,5032,5039,5045,5051,5057,5063,5070,5076,5082,5088,5094,5101, +5107,5113,5119,5125,5132,5138,5144,5150,5157,5163,5169,5175,5182,5188,5194,5201,5207,5213,5219,5226,5232,5238,5244,5251,5257,5263,5270,5276,5282,5289,5295,5301,5308,5314,5320,5327,5333,5339,5346,5352,5358,5365,5371,5377,5384,5390,5396,5403,5409,5415, +5422,5428,5435,5441,5447,5454,5460,5467,5473,5479,5486,5492,5499,5505,5511,5518,5524,5531,5537,5544,5550,5556,5563,5569,5576,5582,5589,5595,5602,5608,5614,5621,5627,5634,5640,5647,5653,5660,5666,5673,5679,5686,5692,5699,5705,5712,5718,5725,5731,5738, +5744,5751,5757,5764,5770,5777,5784,5790,5797,5803,5810,5816,5823,5829,5836,5843,5849,5856,5862,5869,5875,5882,5889,5895,5902,5908,5915,5922,5928,5935,5941,5948,5955,5961,5968,5974,5981,5988,5994,6001,6008,6014,6021,6028,6034,6041,6048,6054,6061,6067, +6074,6081,6088,6094,6101,6108,6114,6121,6128,6134,6141,6148,6154,6161,6168,6174,6181,6188,6195,6201,6208,6215,6222,6228,6235,6242,6248,6255,6262,6269,6275,6282,6289,6296,6302,6309,6316,6323,6330,6336,6343,6350,6357,6363,6370,6377,6384,6391,6397,6404, +6411,6418,6425,6431,6438,6445,6452,6459,6466,6472,6479,6486,6493,6500,6507,6513,6520,6527,6534,6541,6548,6555,6561,6568,6575,6582,6589,6596,6603,6610,6616,6623,6630,6637,6644,6651,6658,6665,6672,6679,6685,6692,6699,6706,6713,6720,6727,6734,6741,6748, +6755,6762,6769,6776,6783,6790,6796,6803,6810,6817,6824,6831,6838,6845,6852,6859,6866,6873,6880,6887,6894,6901,6908,6915,6922,6929,6936,6943,6950,6957,6964,6971,6978,6985,6992,6999,7006,7013,7020,7027,7035,7042,7049,7056,7063,7070,7077,7084,7091,7098, +7105,7112,7119,7126,7133,7140,7148,7155,7162,7169,7176,7183,7190,7197,7204,7211,7219,7226,7233,7240,7247,7254,7261,7268,7276,7283,7290,7297,7304,7311,7318,7326,7333,7340,7347,7354,7361,7368,7376,7383,7390,7397,7404,7411,7419,7426,7433,7440,7447,7455, +7462,7469,7476,7483,7491,7498,7505,7512,7519,7527,7534,7541,7548,7556,7563,7570,7577,7584,7592,7599,7606,7613,7621,7628,7635,7642,7650,7657,7664,7671,7679,7686,7693,7701,7708,7715,7722,7730,7737,7744,7752,7759,7766,7773,7781,7788,7795,7803,7810,7817, +7825,7832,7839,7847,7854,7861,7869,7876,7883,7891,7898,7905,7913,7920,7927,7935,7942,7949,7957,7964,7971,7979,7986,7993,8001,8008,8016,8023,8030,8038,8045,8052,8060,8067,8075,8082,8089,8097,8104,8112,8119,8126,8134,8141,8149,8156,8164,8171,8178,8186, +8193,8201,8208,8216,8223,8230,8238,8245,8253,8260,8268,8275,8283,8290,8297,8305,8312,8320,8327,8335,8342,8350,8357,8365,8372,8380,8387,8395,8402,8410,8417,8425,8432,8440,8447,8455,8462,8470,8477,8485,8492,8500,8507,8515,8522,8530,8537,8545,8552,8560, +8568,8575,8583,8590,8598,8605,8613,8620,8628,8635,8643,8651,8658,8666,8673,8681,8688,8696,8704,8711,8719,8726,8734,8742,8749,8757,8764,8772,8779,8787,8795,8802,8810,8817,8825,8833,8840,8848,8856,8863,8871,8878,8886,8894,8901,8909,8917,8924,8932,8940, +8947,8955,8962,8970,8978,8985,8993,9001,9008,9016,9024,9031,9039,9047,9054,9062,9070,9077,9085,9093,9100,9108,9116,9124,9131,9139,9147,9154,9162,9170,9177,9185,9193,9201,9208,9216,9224,9231,9239,9247,9255,9262,9270,9278,9285,9293,9301,9309,9316,9324, +9332,9340,9347,9355,9363,9371,9378,9386,9394,9402,9409,9417,9425,9433,9440,9448,9456,9464,9472,9479,9487,9495,9503,9511,9518,9526,9534,9542,9549,9557,9565,9573,9581,9588,9596,9604,9612,9620,9628,9635,9643,9651,9659,9667,9674,9682,9690,9698,9706,9714, +9721,9729,9737,9745,9753,9761,9769,9776,9784,9792,9800,9808,9816,9824,9831,9839,9847,9855,9863,9871,9879,9886,9894,9902,9910,9918,9926,9934,9942,9950,9957,9965,9973,9981,9989,9997,10005,10013,10021,10029,10036,10044,10052,10060,10068,10076,10084,10092,10100,10108, +10116,10124,10131,10139,10147,10155,10163,10171,10179,10187,10195,10203,10211,10219,10227,10235,10243,10251,10259,10267,10274,10282,10290,10298,10306,10314,10322,10330,10338,10346,10354,10362,10370,10378,10386,10394,10402,10410,10418,10426,10434,10442,10450,10458,10466,10474,10482,10490,10498,10506, +10514,10522,10530,10538,10546,10554,10562,10570,10578,10586,10594,10602,10610,10618,10626,10634,10642,10650,10658,10667,10675,10683,10691,10699,10707,10715,10723,10731,10739,10747,10755,10763,10771,10779,10787,10795,10803,10811,10820,10828,10836,10844,10852,10860,10868,10876,10884,10892,10900,10908, +10916,10925,10933,10941,10949,10957,10965,10973,10981,10989,10997,11006,11014,11022,11030,11038,11046,11054,11062,11070,11079,11087,11095,11103,11111,11119,11127,11135,11144,11152,11160,11168,11176,11184,11192,11200,11209,11217,11225,11233,11241,11249,11257,11266,11274,11282,11290,11298,11306,11315, +11323,11331,11339,11347,11355,11364,11372,11380,11388,11396,11404,11413,11421,11429,11437,11445,11453,11462,11470,11478,11486,11494,11503,11511,11519,11527,11535,11544,11552,11560,11568,11576,11585,11593,11601,11609,11617,11626,11634,11642,11650,11658,11667,11675,11683,11691,11699,11708,11716,11724, +11732,11741,11749,11757,11765,11774,11782,11790,11798,11806,11815,11823,11831,11839,11848,11856,11864,11872,11881,11889,11897,11905,11914,11922,11930,11938,11947,11955,11963,11971,11980,11988,11996,12005,12013,12021,12029,12038,12046,12054,12062,12071,12079,12087,12096,12104,12112,12120,12129,12137, +12145,12154,12162,12170,12178,12187,12195,12203,12212,12220,12228,12236,12245,12253,12261,12270,12278,12286,12295,12303,12311,12320,12328,12336,12344,12353,12361,12369,12378,12386,12394,12403,12411,12419,12428,12436,12444,12453,12461,12469,12478,12486,12494,12503,12511,12519,12528,12536,12544,12553, +12561,12569,12578,12586,12594,12603,12611,12619,12628,12636,12644,12653,12661,12670,12678,12686,12695,12703,12711,12720,12728,12736,12745,12753,12762,12770,12778,12787,12795,12803,12812,12820,12828,12837,12845,12854,12862,12870,12879,12887,12896,12904,12912,12921,12929,12937,12946,12954,12963,12971, +12979,12988,12996,13005,13013,13021,13030,13038,13047,13055,13063,13072,13080,13089,13097,13105,13114,13122,13131,13139,13147,13156,13164,13173,13181,13189,13198,13206,13215,13223,13232,13240,13248,13257,13265,13274,13282,13291,13299,13307,13316,13324,13333,13341,13350,13358,13366,13375,13383,13392, +13400,13409,13417,13425,13434,13442,13451,13459,13468,13476,13485,13493,13501,13510,13518,13527,13535,13544,13552,13561,13569,13577,13586,13594,13603,13611,13620,13628,13637,13645,13654,13662,13670,13679,13687,13696,13704,13713,13721,13730,13738,13747,13755,13764,13772,13781,13789,13797,13806,13814, +13823,13831,13840,13848,13857,13865,13874,13882,13891,13899,13908,13916,13925,13933,13942,13950,13959,13967,13976,13984,13992,14001,14009,14018,14026,14035,14043,14052,14060,14069,14077,14086,14094,14103,14111,14120,14128,14137,14145,14154,14162,14171,14179,14188,14196,14205,14213,14222,14230,14239, +14247,14256,14264,14273,14281,14290,14298,14307,14315,14324,14332,14341,14350,14358,14367,14375,14384,14392,14401,14409,14418,14426,14435,14443,14452,14460,14469,14477,14486,14494,14503,14511,14520,14528,14537,14545,14554,14563,14571,14580,14588,14597,14605,14614,14622,14631,14639,14648,14656,14665, +14673,14682,14690,14699,14708,14716,14725,14733,14742,14750,14759,14767,14776,14784,14793,14801,14810,14819,14827,14836,14844,14853,14861,14870,14878,14887,14895,14904,14912,14921,14930,14938,14947,14955,14964,14972,14981,14989,14998,15006,15015,15024,15032,15041,15049,15058,15066,15075,15083,15092, +15101,15109,15118,15126,15135,15143,15152,15160,15169,15178,15186,15195,15203,15212,15220,15229,15237,15246,15255,15263,15272,15280,15289,15297,15306,15314,15323,15332,15340,15349,15357,15366,15374,15383,15392,15400,15409,15417,15426,15434,15443,15451,15460,15469,15477,15486,15494,15503,15511,15520, +15529,15537,15546,15554,15563,15571,15580,15589,15597,15606,15614,15623,15631,15640,15649,15657,15666,15674,15683,15691,15700,15709,15717,15726,15734,15743,15751,15760,15769,15777,15786,15794,15803,15811,15820,15829,15837,15846,15854,15863,15871,15880,15889,15897,15906,15914,15923,15931,15940,15949, +15957,15966,15974,15983,15992,16000,16009,16017,16026,16034,16043,16052,16060,16069,16077,16086,16094,16103,16112,16120,16129,16137,16146,16155,16163,16172,16180,16189,16197,16206,16215,16223,16232,16240,16249,16257,16266,16275,16283,16292,16300,16309,16318,16326,16335,16343,16352,16360,16369,16378, +16386,16395,16403,16412,16420,16429,16438,16446,16455,16463,16472,16481,16489,16498,16506,16515,16523,16532,16541,16549,16558,16566,16575,16583,16592,16601,16609,16618,16626,16635,16644,16652,16661,16669,16678,16686,16695,16704,16712,16721,16729,16738,16746,16755,16764,16772,16781,16789,16798,16806, +16815,16824,16832,16841,16849,16858,16867,16875,16884,16892,16901,16909,16918,16927,16935,16944,16952,16961,16969,16978,16987,16995,17004,17012,17021,17029,17038,17047,17055,17064,17072,17081,17089,17098,17107,17115,17124,17132,17141,17149,17158,17167,17175,17184,17192,17201,17209,17218,17227,17235, +17244,17252,17261,17269,17278,17287,17295,17304,17312,17321,17329,17338,17346,17355,17364,17372,17381,17389,17398,17406,17415,17424,17432,17441,17449,17458,17466,17475,17483,17492,17501,17509,17518,17526,17535,17543,17552,17561,17569,17578,17586,17595,17603,17612,17620,17629,17638,17646,17655,17663, +17672,17680,17689,17697,17706,17714,17723,17732,17740,17749,17757,17766,17774,17783,17791,17800,17809,17817,17826,17834,17843,17851,17860,17868,17877,17885,17894,17903,17911,17920,17928,17937,17945,17954,17962,17971,17979,17988,17996,18005,18014,18022,18031,18039,18048,18056,18065,18073,18082,18090, +18099,18107,18116,18124,18133,18142,18150,18159,18167,18176,18184,18193,18201,18210,18218,18227,18235,18244,18252,18261,18269,18278,18286,18295,18304,18312,18321,18329,18338,18346,18355,18363,18372,18380,18389,18397,18406,18414,18423,18431,18440,18448,18457,18465,18474,18482,18491,18499,18508,18516, +18525,18533,18542,18550,18559,18567,18576,18584,18593,18601,18610,18618,18627,18635,18644,18652,18661,18669,18678,18686,18695,18703,18712,18720,18729,18737,18746,18754,18763,18771,18780,18788,18797,18805,18814,18822,18831,18839,18848,18856,18865,18873,18882,18890,18898,18907,18915,18924,18932,18941, +18949,18958,18966,18975,18983,18992,19000,19009,19017,19026,19034,19043,19051,19059,19068,19076,19085,19093,19102,19110,19119,19127,19136,19144,19153,19161,19169,19178,19186,19195,19203,19212,19220,19229,19237,19245,19254,19262,19271,19279,19288,19296,19305,19313,19321,19330,19338,19347,19355,19364, +19372,19381,19389,19397,19406,19414,19423,19431,19440,19448,19456,19465,19473,19482,19490,19499,19507,19515,19524,19532,19541,19549,19557,19566,19574,19583,19591,19600,19608,19616,19625,19633,19642,19650,19658,19667,19675,19684,19692,19700,19709,19717,19726,19734,19742,19751,19759,19768,19776,19784, +19793,19801,19810,19818,19826,19835,19843,19852,19860,19868,19877,19885,19893,19902,19910,19919,19927,19935,19944,19952,19960,19969,19977,19986,19994,20002,20011,20019,20027,20036,20044,20052,20061,20069,20078,20086,20094,20103,20111,20119,20128,20136,20144,20153,20161,20169,20178,20186,20194,20203, +20211,20220,20228,20236,20245,20253,20261,20270,20278,20286,20295,20303,20311,20320,20328,20336,20345,20353,20361,20369,20378,20386,20394,20403,20411,20419,20428,20436,20444,20453,20461,20469,20478,20486,20494,20502,20511,20519,20527,20536,20544,20552,20561,20569,20577,20585,20594,20602,20610,20619, +20627,20635,20643,20652,20660,20668,20677,20685,20693,20701,20710,20718,20726,20735,20743,20751,20759,20768,20776,20784,20792,20801,20809,20817,20825,20834,20842,20850,20858,20867,20875,20883,20891,20900,20908,20916,20924,20933,20941,20949,20957,20966,20974,20982,20990,20999,21007,21015,21023,21032, +21040,21048,21056,21064,21073,21081,21089,21097,21106,21114,21122,21130,21138,21147,21155,21163,21171,21179,21188,21196,21204,21212,21220,21229,21237,21245,21253,21261,21270,21278,21286,21294,21302,21310,21319,21327,21335,21343,21351,21360,21368,21376,21384,21392,21400,21409,21417,21425,21433,21441, +21449,21458,21466,21474,21482,21490,21498,21506,21515,21523,21531,21539,21547,21555,21563,21572,21580,21588,21596,21604,21612,21620,21629,21637,21645,21653,21661,21669,21677,21685,21694,21702,21710,21718,21726,21734,21742,21750,21758,21767,21775,21783,21791,21799,21807,21815,21823,21831,21839,21847, +21856,21864,21872,21880,21888,21896,21904,21912,21920,21928,21936,21944,21952,21961,21969,21977,21985,21993,22001,22009,22017,22025,22033,22041,22049,22057,22065,22073,22081,22089,22097,22106,22114,22122,22130,22138,22146,22154,22162,22170,22178,22186,22194,22202,22210,22218,22226,22234,22242,22250, +22258,22266,22274,22282,22290,22298,22306,22314,22322,22330,22338,22346,22354,22362,22370,22378,22386,22394,22402,22410,22418,22426,22434,22442,22450,22458,22466,22474,22482,22490,22498,22505,22513,22521,22529,22537,22545,22553,22561,22569,22577,22585,22593,22601,22609,22617,22625,22633,22641,22648, +22656,22664,22672,22680,22688,22696,22704,22712,22720,22728,22736,22743,22751,22759,22767,22775,22783,22791,22799,22807,22815,22822,22830,22838,22846,22854,22862,22870,22878,22885,22893,22901,22909,22917,22925,22933,22941,22948,22956,22964,22972,22980,22988,22996,23003,23011,23019,23027,23035,23043, +23050,23058,23066,23074,23082,23090,23097,23105,23113,23121,23129,23137,23144,23152,23160,23168,23176,23183,23191,23199,23207,23215,23222,23230,23238,23246,23254,23261,23269,23277,23285,23293,23300,23308,23316,23324,23331,23339,23347,23355,23362,23370,23378,23386,23393,23401,23409,23417,23424,23432, +23440,23448,23455,23463,23471,23479,23486,23494,23502,23510,23517,23525,23533,23540,23548,23556,23564,23571,23579,23587,23594,23602,23610,23618,23625,23633,23641,23648,23656,23664,23671,23679,23687,23694,23702,23710,23717,23725,23733,23740,23748,23756,23763,23771,23779,23786,23794,23802,23809,23817, +23825,23832,23840,23848,23855,23863,23870,23878,23886,23893,23901,23909,23916,23924,23931,23939,23947,23954,23962,23970,23977,23985,23992,24000,24008,24015,24023,24030,24038,24045,24053,24061,24068,24076,24083,24091,24098,24106,24114,24121,24129,24136,24144,24151,24159,24167,24174,24182,24189,24197, +24204,24212,24219,24227,24234,24242,24249,24257,24264,24272,24280,24287,24295,24302,24310,24317,24325,24332,24340,24347,24355,24362,24370,24377,24385,24392,24400,24407,24414,24422,24429,24437,24444,24452,24459,24467,24474,24482,24489,24497,24504,24512,24519,24526,24534,24541,24549,24556,24564,24571, +24578,24586,24593,24601,24608,24616,24623,24630,24638,24645,24653,24660,24667,24675,24682,24690,24697,24704,24712,24719,24727,24734,24741,24749,24756,24763,24771,24778,24786,24793,24800,24808,24815,24822,24830,24837,24844,24852,24859,24866,24874,24881,24888,24896,24903,24910,24918,24925,24932,24940, +24947,24954,24962,24969,24976,24984,24991,24998,25006,25013,25020,25027,25035,25042,25049,25057,25064,25071,25078,25086,25093,25100,25107,25115,25122,25129,25136,25144,25151,25158,25165,25173,25180,25187,25194,25202,25209,25216,25223,25231,25238,25245,25252,25259,25267,25274,25281,25288,25295,25303, +25310,25317,25324,25331,25339,25346,25353,25360,25367,25374,25382,25389,25396,25403,25410,25417,25425,25432,25439,25446,25453,25460,25468,25475,25482,25489,25496,25503,25510,25517,25525,25532,25539,25546,25553,25560,25567,25574,25581,25589,25596,25603,25610,25617,25624,25631,25638,25645,25652,25659, +25666,25674,25681,25688,25695,25702,25709,25716,25723,25730,25737,25744,25751,25758,25765,25772,25779,25786,25793,25800,25807,25814,25821,25828,25835,25842,25849,25856,25863,25870,25877,25884,25891,25898,25905,25912,25919,25926,25933,25940,25947,25954,25961,25968,25975,25982,25989,25996,26003,26010, +26017,26024,26031,26038,26044,26051,26058,26065,26072,26079,26086,26093,26100,26107,26114,26121,26127,26134,26141,26148,26155,26162,26169,26176,26182,26189,26196,26203,26210,26217,26224,26231,26237,26244,26251,26258,26265,26272,26278,26285,26292,26299,26306,26313,26319,26326,26333,26340,26347,26354, +26360,26367,26374,26381,26388,26394,26401,26408,26415,26421,26428,26435,26442,26449,26455,26462,26469,26476,26482,26489,26496,26503,26509,26516,26523,26530,26536,26543,26550,26557,26563,26570,26577,26583,26590,26597,26604,26610,26617,26624,26630,26637,26644,26650,26657,26664,26670,26677,26684,26691, +26697,26704,26711,26717,26724,26730,26737,26744,26750,26757,26764,26770,26777,26784,26790,26797,26803,26810,26817,26823,26830,26837,26843,26850,26856,26863,26870,26876,26883,26889,26896,26902,26909,26916,26922,26929,26935,26942,26948,26955,26962,26968,26975,26981,26988,26994,27001,27007,27014,27020, +27027,27033,27040,27046,27053,27059,27066,27073,27079,27086,27092,27098,27105,27111,27118,27124,27131,27137,27144,27150,27157,27163,27170,27176,27183,27189,27196,27202,27208,27215,27221,27228,27234,27241,27247,27253,27260,27266,27273,27279,27285,27292,27298,27305,27311,27317,27324,27330,27337,27343, +27349,27356,27362,27368,27375,27381,27388,27394,27400,27407,27413,27419,27426,27432,27438,27445,27451,27457,27464,27470,27476,27483,27489,27495,27501,27508,27514,27520,27527,27533,27539,27546,27552,27558,27564,27571,27577,27583,27589,27596,27602,27608,27614,27621,27627,27633,27639,27646,27652,27658, +27664,27671,27677,27683,27689,27695,27702,27708,27714,27720,27726,27733,27739,27745,27751,27757,27763,27770,27776,27782,27788,27794,27800,27807,27813,27819,27825,27831,27837,27843,27850,27856,27862,27868,27874,27880,27886,27892,27898,27905,27911,27917,27923,27929,27935,27941,27947,27953,27959,27965, +27972,27978,27984,27990,27996,28002,28008,28014,28020,28026,28032,28038,28044,28050,28056,28062,28068,28074,28080,28086,28092,28098,28104,28110,28116,28122,28128,28134,28140,28146,28152,28158,28164,28170,28176,28182,28188,28194,28200,28206,28212,28218,28223,28229,28235,28241,28247,28253,28259,28265, +28271,28277,28283,28288,28294,28300,28306,28312,28318,28324,28330,28336,28341,28347,28353,28359,28365,28371,28377,28382,28388,28394,28400,28406,28412,28417,28423,28429,28435,28441,28446,28452,28458,28464,28470,28475,28481,28487,28493,28499,28504,28510,28516,28522,28527,28533,28539,28545,28550,28556, +28562,28568,28573,28579,28585,28591,28596,28602,28608,28613,28619,28625,28631,28636,28642,28648,28653,28659,28665,28670,28676,28682,28687,28693,28699,28704,28710,28716,28721,28727,28733,28738,28744,28749,28755,28761,28766,28772,28778,28783,28789,28794,28800,28806,28811,28817,28822,28828,28834,28839, +28845,28850,28856,28861,28867,28872,28878,28884,28889,28895,28900,28906,28911,28917,28922,28928,28933,28939,28944,28950,28955,28961,28966,28972,28977,28983,28988,28994,28999,29005,29010,29016,29021,29027,29032,29038,29043,29048,29054,29059,29065,29070,29076,29081,29086,29092,29097,29103,29108,29113, +29119,29124,29130,29135,29140,29146,29151,29157,29162,29167,29173,29178,29183,29189,29194,29199,29205,29210,29215,29221,29226,29231,29237,29242,29247,29253,29258,29263,29269,29274,29279,29285,29290,29295,29300,29306,29311,29316,29321,29327,29332,29337,29342,29348,29353,29358,29363,29369,29374,29379, +29384,29390,29395,29400,29405,29410,29416,29421,29426,29431,29436,29442,29447,29452,29457,29462,29467,29473,29478,29483,29488,29493,29498,29504,29509,29514,29519,29524,29529,29534,29539,29545,29550,29555,29560,29565,29570,29575,29580,29585,29590,29595,29601,29606,29611,29616,29621,29626,29631,29636, +29641,29646,29651,29656,29661,29666,29671,29676,29681,29686,29691,29696,29701,29706,29711,29716,29721,29726,29731,29736,29741,29746,29751,29756,29761,29766,29771,29776,29781,29786,29791,29795,29800,29805,29810,29815,29820,29825,29830,29835,29840,29845,29849,29854,29859,29864,29869,29874,29879,29884, +29888,29893,29898,29903,29908,29913,29918,29922,29927,29932,29937,29942,29946,29951,29956,29961,29966,29970,29975,29980,29985,29990,29994,29999,30004,30009,30013,30018,30023,30028,30033,30037,30042,30047,30051,30056,30061,30066,30070,30075,30080,30084,30089,30094,30099,30103,30108,30113,30117,30122, +30127,30131,30136,30141,30145,30150,30155,30159,30164,30169,30173,30178,30182,30187,30192,30196,30201,30206,30210,30215,30219,30224,30229,30233,30238,30242,30247,30251,30256,30261,30265,30270,30274,30279,30283,30288,30292,30297,30301,30306,30311,30315,30320,30324,30329,30333,30338,30342,30347,30351, +30356,30360,30364,30369,30373,30378,30382,30387,30391,30396,30400,30405,30409,30413,30418,30422,30427,30431,30436,30440,30444,30449,30453,30458,30462,30466,30471,30475,30479,30484,30488,30493,30497,30501,30506,30510,30514,30519,30523,30527,30532,30536,30540,30545,30549,30553,30558,30562,30566,30570, +30575,30579,30583,30588,30592,30596,30600,30605,30609,30613,30617,30622,30626,30630,30634,30639,30643,30647,30651,30656,30660,30664,30668,30672,30677,30681,30685,30689,30693,30697,30702,30706,30710,30714,30718,30722,30727,30731,30735,30739,30743,30747,30751,30756,30760,30764,30768,30772,30776,30780, +30784,30788,30792,30797,30801,30805,30809,30813,30817,30821,30825,30829,30833,30837,30841,30845,30849,30853,30857,30861,30865,30869,30873,30877,30881,30885,30889,30893,30897,30901,30905,30909,30913,30917,30921,30925,30929,30933,30937,30941,30945,30949,30953,30957,30960,30964,30968,30972,30976,30980, +30984,30988,30992,30996,30999,31003,31007,31011,31015,31019,31023,31026,31030,31034,31038,31042,31046,31050,31053,31057,31061,31065,31069,31072,31076,31080,31084,31088,31091,31095,31099,31103,31106,31110,31114,31118,31121,31125,31129,31133,31136,31140,31144,31148,31151,31155,31159,31162,31166,31170, +31174,31177,31181,31185,31188,31192,31196,31199,31203,31207,31210,31214,31218,31221,31225,31228,31232,31236,31239,31243,31247,31250,31254,31257,31261,31265,31268,31272,31275,31279,31282,31286,31290,31293,31297,31300,31304,31307,31311,31314,31318,31321,31325,31329,31332,31336,31339,31343,31346,31350, +31353,31357,31360,31363,31367,31370,31374,31377,31381,31384,31388,31391,31395,31398,31401,31405,31408,31412,31415,31419,31422,31425,31429,31432,31436,31439,31442,31446,31449,31452,31456,31459,31463,31466,31469,31473,31476,31479,31483,31486,31489,31493,31496,31499,31503,31506,31509,31512,31516,31519, +31522,31526,31529,31532,31535,31539,31542,31545,31548,31552,31555,31558,31561,31565,31568,31571,31574,31577,31581,31584,31587,31590,31593,31597,31600,31603,31606,31609,31613,31616,31619,31622,31625,31628,31631,31635,31638,31641,31644,31647,31650,31653,31656,31660,31663,31666,31669,31672,31675,31678, +31681,31684,31687,31690,31693,31696,31700,31703,31706,31709,31712,31715,31718,31721,31724,31727,31730,31733,31736,31739,31742,31745,31748,31751,31754,31757,31760,31763,31766,31768,31771,31774,31777,31780,31783,31786,31789,31792,31795,31798,31801,31804,31806,31809,31812,31815,31818,31821,31824,31827, +31830,31832,31835,31838,31841,31844,31847,31849,31852,31855,31858,31861,31864,31866,31869,31872,31875,31878,31880,31883,31886,31889,31891,31894,31897,31900,31902,31905,31908,31911,31913,31916,31919,31922,31924,31927,31930,31933,31935,31938,31941,31943,31946,31949,31951,31954,31957,31959,31962,31965, +31967,31970,31973,31975,31978,31980,31983,31986,31988,31991,31994,31996,31999,32001,32004,32007,32009,32012,32014,32017,32019,32022,32025,32027,32030,32032,32035,32037,32040,32042,32045,32047,32050,32052,32055,32057,32060,32062,32065,32067,32070,32072,32075,32077,32080,32082,32085,32087,32090,32092, +32094,32097,32099,32102,32104,32107,32109,32111,32114,32116,32119,32121,32123,32126,32128,32130,32133,32135,32138,32140,32142,32145,32147,32149,32152,32154,32156,32159,32161,32163,32166,32168,32170,32172,32175,32177,32179,32182,32184,32186,32188,32191,32193,32195,32197,32200,32202,32204,32206,32209, +32211,32213,32215,32217,32220,32222,32224,32226,32228,32231,32233,32235,32237,32239,32241,32244,32246,32248,32250,32252,32254,32256,32259,32261,32263,32265,32267,32269,32271,32273,32275,32277,32280,32282,32284,32286,32288,32290,32292,32294,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314, +32316,32318,32320,32322,32324,32326,32328,32330,32332,32334,32336,32338,32340,32342,32344,32346,32348,32350,32352,32353,32355,32357,32359,32361,32363,32365,32367,32369,32371,32372,32374,32376,32378,32380,32382,32384,32385,32387,32389,32391,32393,32395,32396,32398,32400,32402,32404,32405,32407,32409, +32411,32413,32414,32416,32418,32420,32421,32423,32425,32427,32428,32430,32432,32434,32435,32437,32439,32440,32442,32444,32446,32447,32449,32451,32452,32454,32456,32457,32459,32461,32462,32464,32466,32467,32469,32470,32472,32474,32475,32477,32479,32480,32482,32483,32485,32487,32488,32490,32491,32493, +32494,32496,32497,32499,32501,32502,32504,32505,32507,32508,32510,32511,32513,32514,32516,32517,32519,32520,32522,32523,32525,32526,32528,32529,32531,32532,32533,32535,32536,32538,32539,32541,32542,32544,32545,32546,32548,32549,32551,32552,32553,32555,32556,32557,32559,32560,32562,32563,32564,32566, +32567,32568,32570,32571,32572,32574,32575,32576,32578,32579,32580,32581,32583,32584,32585,32587,32588,32589,32590,32592,32593,32594,32595,32597,32598,32599,32600,32601,32603,32604,32605,32606,32608,32609,32610,32611,32612,32613,32615,32616,32617,32618,32619,32620,32622,32623,32624,32625,32626,32627, +32628,32629,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32670,32671,32672,32673,32674,32675,32676,32677,32678, +32679,32680,32680,32681,32682,32683,32684,32685,32686,32687,32687,32688,32689,32690,32691,32692,32692,32693,32694,32695,32696,32696,32697,32698,32699,32700,32700,32701,32702,32703,32704,32704,32705,32706,32707,32707,32708,32709,32709,32710,32711,32712,32712,32713,32714,32714,32715,32716,32716,32717, +32718,32718,32719,32720,32720,32721,32722,32722,32723,32724,32724,32725,32726,32726,32727,32727,32728,32729,32729,32730,32730,32731,32731,32732,32733,32733,32734,32734,32735,32735,32736,32736,32737,32738,32738,32739,32739,32740,32740,32741,32741,32742,32742,32743,32743,32743,32744,32744,32745,32745, +32746,32746,32747,32747,32748,32748,32748,32749,32749,32750,32750,32750,32751,32751,32752,32752,32752,32753,32753,32753,32754,32754,32755,32755,32755,32756,32756,32756,32757,32757,32757,32757,32758,32758,32758,32759,32759,32759,32760,32760,32760,32760,32761,32761,32761,32761,32762,32762,32762,32762, +32762,32763,32763,32763,32763,32764,32764,32764,32764,32764,32764,32765,32765,32765,32765,32765,32765,32766,32766,32766,32766,32766,32766,32766,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768, +32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32766,32766,32766,32766,32766,32766,32766,32765,32765,32765,32765,32765,32765,32764,32764,32764,32764,32764,32764,32763,32763,32763,32763,32762, +32762,32762,32762,32762,32761,32761,32761,32761,32760,32760,32760,32760,32759,32759,32759,32758,32758,32758,32757,32757,32757,32757,32756,32756,32756,32755,32755,32755,32754,32754,32753,32753,32753,32752,32752,32752,32751,32751,32750,32750,32750,32749,32749,32748,32748,32748,32747,32747,32746,32746, +32745,32745,32744,32744,32743,32743,32743,32742,32742,32741,32741,32740,32740,32739,32739,32738,32738,32737,32736,32736,32735,32735,32734,32734,32733,32733,32732,32731,32731,32730,32730,32729,32729,32728,32727,32727,32726,32726,32725,32724,32724,32723,32722,32722,32721,32720,32720,32719,32718,32718, +32717,32716,32716,32715,32714,32714,32713,32712,32712,32711,32710,32709,32709,32708,32707,32707,32706,32705,32704,32704,32703,32702,32701,32700,32700,32699,32698,32697,32696,32696,32695,32694,32693,32692,32692,32691,32690,32689,32688,32687,32687,32686,32685,32684,32683,32682,32681,32680,32680,32679, +32678,32677,32676,32675,32674,32673,32672,32671,32670,32670,32669,32668,32667,32666,32665,32664,32663,32662,32661,32660,32659,32658,32657,32656,32655,32654,32653,32652,32651,32650,32649,32648,32647,32646,32645,32644,32643,32641,32640,32639,32638,32637,32636,32635,32634,32633,32632,32631,32629,32628, +32627,32626,32625,32624,32623,32622,32620,32619,32618,32617,32616,32615,32613,32612,32611,32610,32609,32608,32606,32605,32604,32603,32601,32600,32599,32598,32597,32595,32594,32593,32592,32590,32589,32588,32587,32585,32584,32583,32581,32580,32579,32578,32576,32575,32574,32572,32571,32570,32568,32567, +32566,32564,32563,32562,32560,32559,32557,32556,32555,32553,32552,32551,32549,32548,32546,32545,32544,32542,32541,32539,32538,32536,32535,32533,32532,32531,32529,32528,32526,32525,32523,32522,32520,32519,32517,32516,32514,32513,32511,32510,32508,32507,32505,32504,32502,32501,32499,32497,32496,32494, +32493,32491,32490,32488,32487,32485,32483,32482,32480,32479,32477,32475,32474,32472,32470,32469,32467,32466,32464,32462,32461,32459,32457,32456,32454,32452,32451,32449,32447,32446,32444,32442,32440,32439,32437,32435,32434,32432,32430,32428,32427,32425,32423,32421,32420,32418,32416,32414,32413,32411, +32409,32407,32405,32404,32402,32400,32398,32396,32395,32393,32391,32389,32387,32385,32384,32382,32380,32378,32376,32374,32372,32371,32369,32367,32365,32363,32361,32359,32357,32355,32353,32352,32350,32348,32346,32344,32342,32340,32338,32336,32334,32332,32330,32328,32326,32324,32322,32320,32318,32316, +32314,32312,32310,32308,32306,32304,32302,32300,32298,32296,32294,32292,32290,32288,32286,32284,32282,32280,32277,32275,32273,32271,32269,32267,32265,32263,32261,32259,32256,32254,32252,32250,32248,32246,32244,32241,32239,32237,32235,32233,32231,32228,32226,32224,32222,32220,32217,32215,32213,32211, +32209,32206,32204,32202,32200,32197,32195,32193,32191,32188,32186,32184,32182,32179,32177,32175,32172,32170,32168,32166,32163,32161,32159,32156,32154,32152,32149,32147,32145,32142,32140,32138,32135,32133,32130,32128,32126,32123,32121,32119,32116,32114,32111,32109,32107,32104,32102,32099,32097,32094, +32092,32090,32087,32085,32082,32080,32077,32075,32072,32070,32067,32065,32062,32060,32057,32055,32052,32050,32047,32045,32042,32040,32037,32035,32032,32030,32027,32025,32022,32019,32017,32014,32012,32009,32007,32004,32001,31999,31996,31994,31991,31988,31986,31983,31980,31978,31975,31973,31970,31967, +31965,31962,31959,31957,31954,31951,31949,31946,31943,31941,31938,31935,31933,31930,31927,31924,31922,31919,31916,31913,31911,31908,31905,31902,31900,31897,31894,31891,31889,31886,31883,31880,31878,31875,31872,31869,31866,31864,31861,31858,31855,31852,31849,31847,31844,31841,31838,31835,31832,31830, +31827,31824,31821,31818,31815,31812,31809,31806,31804,31801,31798,31795,31792,31789,31786,31783,31780,31777,31774,31771,31768,31766,31763,31760,31757,31754,31751,31748,31745,31742,31739,31736,31733,31730,31727,31724,31721,31718,31715,31712,31709,31706,31703,31700,31696,31693,31690,31687,31684,31681, +31678,31675,31672,31669,31666,31663,31660,31656,31653,31650,31647,31644,31641,31638,31635,31631,31628,31625,31622,31619,31616,31613,31609,31606,31603,31600,31597,31593,31590,31587,31584,31581,31577,31574,31571,31568,31565,31561,31558,31555,31552,31548,31545,31542,31539,31535,31532,31529,31526,31522, +31519,31516,31512,31509,31506,31503,31499,31496,31493,31489,31486,31483,31479,31476,31473,31469,31466,31463,31459,31456,31452,31449,31446,31442,31439,31436,31432,31429,31425,31422,31419,31415,31412,31408,31405,31401,31398,31395,31391,31388,31384,31381,31377,31374,31370,31367,31363,31360,31357,31353, +31350,31346,31343,31339,31336,31332,31329,31325,31321,31318,31314,31311,31307,31304,31300,31297,31293,31290,31286,31282,31279,31275,31272,31268,31265,31261,31257,31254,31250,31247,31243,31239,31236,31232,31228,31225,31221,31218,31214,31210,31207,31203,31199,31196,31192,31188,31185,31181,31177,31174, +31170,31166,31162,31159,31155,31151,31148,31144,31140,31136,31133,31129,31125,31121,31118,31114,31110,31106,31103,31099,31095,31091,31088,31084,31080,31076,31072,31069,31065,31061,31057,31053,31050,31046,31042,31038,31034,31030,31026,31023,31019,31015,31011,31007,31003,30999,30996,30992,30988,30984, +30980,30976,30972,30968,30964,30960,30957,30953,30949,30945,30941,30937,30933,30929,30925,30921,30917,30913,30909,30905,30901,30897,30893,30889,30885,30881,30877,30873,30869,30865,30861,30857,30853,30849,30845,30841,30837,30833,30829,30825,30821,30817,30813,30809,30805,30801,30797,30792,30788,30784, +30780,30776,30772,30768,30764,30760,30756,30751,30747,30743,30739,30735,30731,30727,30722,30718,30714,30710,30706,30702,30697,30693,30689,30685,30681,30677,30672,30668,30664,30660,30656,30651,30647,30643,30639,30634,30630,30626,30622,30617,30613,30609,30605,30600,30596,30592,30588,30583,30579,30575, +30570,30566,30562,30558,30553,30549,30545,30540,30536,30532,30527,30523,30519,30514,30510,30506,30501,30497,30493,30488,30484,30479,30475,30471,30466,30462,30458,30453,30449,30444,30440,30436,30431,30427,30422,30418,30413,30409,30405,30400,30396,30391,30387,30382,30378,30373,30369,30364,30360,30356, +30351,30347,30342,30338,30333,30329,30324,30320,30315,30311,30306,30301,30297,30292,30288,30283,30279,30274,30270,30265,30261,30256,30251,30247,30242,30238,30233,30229,30224,30219,30215,30210,30206,30201,30196,30192,30187,30182,30178,30173,30169,30164,30159,30155,30150,30145,30141,30136,30131,30127, +30122,30117,30113,30108,30103,30099,30094,30089,30084,30080,30075,30070,30066,30061,30056,30051,30047,30042,30037,30033,30028,30023,30018,30013,30009,30004,29999,29994,29990,29985,29980,29975,29970,29966,29961,29956,29951,29946,29942,29937,29932,29927,29922,29918,29913,29908,29903,29898,29893,29888, +29884,29879,29874,29869,29864,29859,29854,29849,29845,29840,29835,29830,29825,29820,29815,29810,29805,29800,29795,29791,29786,29781,29776,29771,29766,29761,29756,29751,29746,29741,29736,29731,29726,29721,29716,29711,29706,29701,29696,29691,29686,29681,29676,29671,29666,29661,29656,29651,29646,29641, +29636,29631,29626,29621,29616,29611,29606,29601,29595,29590,29585,29580,29575,29570,29565,29560,29555,29550,29545,29539,29534,29529,29524,29519,29514,29509,29504,29498,29493,29488,29483,29478,29473,29467,29462,29457,29452,29447,29442,29436,29431,29426,29421,29416,29410,29405,29400,29395,29390,29384, +29379,29374,29369,29363,29358,29353,29348,29342,29337,29332,29327,29321,29316,29311,29306,29300,29295,29290,29285,29279,29274,29269,29263,29258,29253,29247,29242,29237,29231,29226,29221,29215,29210,29205,29199,29194,29189,29183,29178,29173,29167,29162,29157,29151,29146,29140,29135,29130,29124,29119, +29113,29108,29103,29097,29092,29086,29081,29076,29070,29065,29059,29054,29048,29043,29038,29032,29027,29021,29016,29010,29005,28999,28994,28988,28983,28977,28972,28966,28961,28955,28950,28944,28939,28933,28928,28922,28917,28911,28906,28900,28895,28889,28884,28878,28872,28867,28861,28856,28850,28845, +28839,28834,28828,28822,28817,28811,28806,28800,28794,28789,28783,28778,28772,28766,28761,28755,28749,28744,28738,28733,28727,28721,28716,28710,28704,28699,28693,28687,28682,28676,28670,28665,28659,28653,28648,28642,28636,28631,28625,28619,28613,28608,28602,28596,28591,28585,28579,28573,28568,28562, +28556,28550,28545,28539,28533,28527,28522,28516,28510,28504,28499,28493,28487,28481,28475,28470,28464,28458,28452,28446,28441,28435,28429,28423,28417,28412,28406,28400,28394,28388,28382,28377,28371,28365,28359,28353,28347,28341,28336,28330,28324,28318,28312,28306,28300,28294,28288,28283,28277,28271, +28265,28259,28253,28247,28241,28235,28229,28223,28218,28212,28206,28200,28194,28188,28182,28176,28170,28164,28158,28152,28146,28140,28134,28128,28122,28116,28110,28104,28098,28092,28086,28080,28074,28068,28062,28056,28050,28044,28038,28032,28026,28020,28014,28008,28002,27996,27990,27984,27978,27972, +27965,27959,27953,27947,27941,27935,27929,27923,27917,27911,27905,27898,27892,27886,27880,27874,27868,27862,27856,27850,27843,27837,27831,27825,27819,27813,27807,27800,27794,27788,27782,27776,27770,27763,27757,27751,27745,27739,27733,27726,27720,27714,27708,27702,27695,27689,27683,27677,27671,27664, +27658,27652,27646,27639,27633,27627,27621,27614,27608,27602,27596,27589,27583,27577,27571,27564,27558,27552,27546,27539,27533,27527,27520,27514,27508,27501,27495,27489,27483,27476,27470,27464,27457,27451,27445,27438,27432,27426,27419,27413,27407,27400,27394,27388,27381,27375,27368,27362,27356,27349, +27343,27337,27330,27324,27317,27311,27305,27298,27292,27285,27279,27273,27266,27260,27253,27247,27241,27234,27228,27221,27215,27208,27202,27196,27189,27183,27176,27170,27163,27157,27150,27144,27137,27131,27124,27118,27111,27105,27098,27092,27086,27079,27073,27066,27059,27053,27046,27040,27033,27027, +27020,27014,27007,27001,26994,26988,26981,26975,26968,26962,26955,26948,26942,26935,26929,26922,26916,26909,26902,26896,26889,26883,26876,26870,26863,26856,26850,26843,26837,26830,26823,26817,26810,26803,26797,26790,26784,26777,26770,26764,26757,26750,26744,26737,26730,26724,26717,26711,26704,26697, +26691,26684,26677,26670,26664,26657,26650,26644,26637,26630,26624,26617,26610,26604,26597,26590,26583,26577,26570,26563,26557,26550,26543,26536,26530,26523,26516,26509,26503,26496,26489,26482,26476,26469,26462,26455,26449,26442,26435,26428,26421,26415,26408,26401,26394,26388,26381,26374,26367,26360, +26354,26347,26340,26333,26326,26319,26313,26306,26299,26292,26285,26278,26272,26265,26258,26251,26244,26237,26231,26224,26217,26210,26203,26196,26189,26182,26176,26169,26162,26155,26148,26141,26134,26127,26121,26114,26107,26100,26093,26086,26079,26072,26065,26058,26051,26044,26038,26031,26024,26017, +26010,26003,25996,25989,25982,25975,25968,25961,25954,25947,25940,25933,25926,25919,25912,25905,25898,25891,25884,25877,25870,25863,25856,25849,25842,25835,25828,25821,25814,25807,25800,25793,25786,25779,25772,25765,25758,25751,25744,25737,25730,25723,25716,25709,25702,25695,25688,25681,25674,25666, +25659,25652,25645,25638,25631,25624,25617,25610,25603,25596,25589,25581,25574,25567,25560,25553,25546,25539,25532,25525,25517,25510,25503,25496,25489,25482,25475,25468,25460,25453,25446,25439,25432,25425,25417,25410,25403,25396,25389,25382,25374,25367,25360,25353,25346,25339,25331,25324,25317,25310, +25303,25295,25288,25281,25274,25267,25259,25252,25245,25238,25231,25223,25216,25209,25202,25194,25187,25180,25173,25165,25158,25151,25144,25136,25129,25122,25115,25107,25100,25093,25086,25078,25071,25064,25057,25049,25042,25035,25027,25020,25013,25006,24998,24991,24984,24976,24969,24962,24954,24947, +24940,24932,24925,24918,24910,24903,24896,24888,24881,24874,24866,24859,24852,24844,24837,24830,24822,24815,24808,24800,24793,24786,24778,24771,24763,24756,24749,24741,24734,24727,24719,24712,24704,24697,24690,24682,24675,24667,24660,24653,24645,24638,24630,24623,24616,24608,24601,24593,24586,24578, +24571,24564,24556,24549,24541,24534,24526,24519,24512,24504,24497,24489,24482,24474,24467,24459,24452,24444,24437,24429,24422,24414,24407,24400,24392,24385,24377,24370,24362,24355,24347,24340,24332,24325,24317,24310,24302,24295,24287,24280,24272,24264,24257,24249,24242,24234,24227,24219,24212,24204, +24197,24189,24182,24174,24167,24159,24151,24144,24136,24129,24121,24114,24106,24098,24091,24083,24076,24068,24061,24053,24045,24038,24030,24023,24015,24008,24000,23992,23985,23977,23970,23962,23954,23947,23939,23931,23924,23916,23909,23901,23893,23886,23878,23870,23863,23855,23848,23840,23832,23825, +23817,23809,23802,23794,23786,23779,23771,23763,23756,23748,23740,23733,23725,23717,23710,23702,23694,23687,23679,23671,23664,23656,23648,23641,23633,23625,23618,23610,23602,23594,23587,23579,23571,23564,23556,23548,23540,23533,23525,23517,23510,23502,23494,23486,23479,23471,23463,23455,23448,23440, +23432,23424,23417,23409,23401,23393,23386,23378,23370,23362,23355,23347,23339,23331,23324,23316,23308,23300,23293,23285,23277,23269,23261,23254,23246,23238,23230,23222,23215,23207,23199,23191,23183,23176,23168,23160,23152,23144,23137,23129,23121,23113,23105,23097,23090,23082,23074,23066,23058,23050, +23043,23035,23027,23019,23011,23003,22996,22988,22980,22972,22964,22956,22948,22941,22933,22925,22917,22909,22901,22893,22885,22878,22870,22862,22854,22846,22838,22830,22822,22815,22807,22799,22791,22783,22775,22767,22759,22751,22743,22736,22728,22720,22712,22704,22696,22688,22680,22672,22664,22656, +22648,22641,22633,22625,22617,22609,22601,22593,22585,22577,22569,22561,22553,22545,22537,22529,22521,22513,22505,22498,22490,22482,22474,22466,22458,22450,22442,22434,22426,22418,22410,22402,22394,22386,22378,22370,22362,22354,22346,22338,22330,22322,22314,22306,22298,22290,22282,22274,22266,22258, +22250,22242,22234,22226,22218,22210,22202,22194,22186,22178,22170,22162,22154,22146,22138,22130,22122,22114,22106,22097,22089,22081,22073,22065,22057,22049,22041,22033,22025,22017,22009,22001,21993,21985,21977,21969,21961,21952,21944,21936,21928,21920,21912,21904,21896,21888,21880,21872,21864,21856, +21847,21839,21831,21823,21815,21807,21799,21791,21783,21775,21767,21758,21750,21742,21734,21726,21718,21710,21702,21694,21685,21677,21669,21661,21653,21645,21637,21629,21620,21612,21604,21596,21588,21580,21572,21563,21555,21547,21539,21531,21523,21515,21506,21498,21490,21482,21474,21466,21458,21449, +21441,21433,21425,21417,21409,21400,21392,21384,21376,21368,21360,21351,21343,21335,21327,21319,21310,21302,21294,21286,21278,21270,21261,21253,21245,21237,21229,21220,21212,21204,21196,21188,21179,21171,21163,21155,21147,21138,21130,21122,21114,21106,21097,21089,21081,21073,21064,21056,21048,21040, +21032,21023,21015,21007,20999,20990,20982,20974,20966,20957,20949,20941,20933,20924,20916,20908,20900,20891,20883,20875,20867,20858,20850,20842,20834,20825,20817,20809,20801,20792,20784,20776,20768,20759,20751,20743,20735,20726,20718,20710,20701,20693,20685,20677,20668,20660,20652,20643,20635,20627, +20619,20610,20602,20594,20585,20577,20569,20561,20552,20544,20536,20527,20519,20511,20502,20494,20486,20478,20469,20461,20453,20444,20436,20428,20419,20411,20403,20394,20386,20378,20369,20361,20353,20345,20336,20328,20320,20311,20303,20295,20286,20278,20270,20261,20253,20245,20236,20228,20220,20211, +20203,20194,20186,20178,20169,20161,20153,20144,20136,20128,20119,20111,20103,20094,20086,20078,20069,20061,20052,20044,20036,20027,20019,20011,20002,19994,19986,19977,19969,19960,19952,19944,19935,19927,19919,19910,19902,19893,19885,19877,19868,19860,19852,19843,19835,19826,19818,19810,19801,19793, +19784,19776,19768,19759,19751,19742,19734,19726,19717,19709,19700,19692,19684,19675,19667,19658,19650,19642,19633,19625,19616,19608,19600,19591,19583,19574,19566,19557,19549,19541,19532,19524,19515,19507,19499,19490,19482,19473,19465,19456,19448,19440,19431,19423,19414,19406,19397,19389,19381,19372, +19364,19355,19347,19338,19330,19321,19313,19305,19296,19288,19279,19271,19262,19254,19245,19237,19229,19220,19212,19203,19195,19186,19178,19169,19161,19153,19144,19136,19127,19119,19110,19102,19093,19085,19076,19068,19059,19051,19043,19034,19026,19017,19009,19000,18992,18983,18975,18966,18958,18949, +18941,18932,18924,18915,18907,18898,18890,18882,18873,18865,18856,18848,18839,18831,18822,18814,18805,18797,18788,18780,18771,18763,18754,18746,18737,18729,18720,18712,18703,18695,18686,18678,18669,18661,18652,18644,18635,18627,18618,18610,18601,18593,18584,18576,18567,18559,18550,18542,18533,18525, +18516,18508,18499,18491,18482,18474,18465,18457,18448,18440,18431,18423,18414,18406,18397,18389,18380,18372,18363,18355,18346,18338,18329,18321,18312,18304,18295,18286,18278,18269,18261,18252,18244,18235,18227,18218,18210,18201,18193,18184,18176,18167,18159,18150,18142,18133,18124,18116,18107,18099, +18090,18082,18073,18065,18056,18048,18039,18031,18022,18014,18005,17996,17988,17979,17971,17962,17954,17945,17937,17928,17920,17911,17903,17894,17885,17877,17868,17860,17851,17843,17834,17826,17817,17809,17800,17791,17783,17774,17766,17757,17749,17740,17732,17723,17714,17706,17697,17689,17680,17672, +17663,17655,17646,17638,17629,17620,17612,17603,17595,17586,17578,17569,17561,17552,17543,17535,17526,17518,17509,17501,17492,17483,17475,17466,17458,17449,17441,17432,17424,17415,17406,17398,17389,17381,17372,17364,17355,17346,17338,17329,17321,17312,17304,17295,17287,17278,17269,17261,17252,17244, +17235,17227,17218,17209,17201,17192,17184,17175,17167,17158,17149,17141,17132,17124,17115,17107,17098,17089,17081,17072,17064,17055,17047,17038,17029,17021,17012,17004,16995,16987,16978,16969,16961,16952,16944,16935,16927,16918,16909,16901,16892,16884,16875,16867,16858,16849,16841,16832,16824,16815, +16806,16798,16789,16781,16772,16764,16755,16746,16738,16729,16721,16712,16704,16695,16686,16678,16669,16661,16652,16644,16635,16626,16618,16609,16601,16592,16583,16575,16566,16558,16549,16541,16532,16523,16515,16506,16498,16489,16481,16472,16463,16455,16446,16438,16429,16420,16412,16403,16395,16386, +16378,16369,16360,16352,16343,16335,16326,16318,16309,16300,16292,16283,16275,16266,16257,16249,16240,16232,16223,16215,16206,16197,16189,16180,16172,16163,16155,16146,16137,16129,16120,16112,16103,16094,16086,16077,16069,16060,16052,16043,16034,16026,16017,16009,16000,15992,15983,15974,15966,15957, +15949,15940,15931,15923,15914,15906,15897,15889,15880,15871,15863,15854,15846,15837,15829,15820,15811,15803,15794,15786,15777,15769,15760,15751,15743,15734,15726,15717,15709,15700,15691,15683,15674,15666,15657,15649,15640,15631,15623,15614,15606,15597,15589,15580,15571,15563,15554,15546,15537,15529, +15520,15511,15503,15494,15486,15477,15469,15460,15451,15443,15434,15426,15417,15409,15400,15392,15383,15374,15366,15357,15349,15340,15332,15323,15314,15306,15297,15289,15280,15272,15263,15255,15246,15237,15229,15220,15212,15203,15195,15186,15178,15169,15160,15152,15143,15135,15126,15118,15109,15101, +15092,15083,15075,15066,15058,15049,15041,15032,15024,15015,15006,14998,14989,14981,14972,14964,14955,14947,14938,14930,14921,14912,14904,14895,14887,14878,14870,14861,14853,14844,14836,14827,14819,14810,14801,14793,14784,14776,14767,14759,14750,14742,14733,14725,14716,14708,14699,14690,14682,14673, +14665,14656,14648,14639,14631,14622,14614,14605,14597,14588,14580,14571,14563,14554,14545,14537,14528,14520,14511,14503,14494,14486,14477,14469,14460,14452,14443,14435,14426,14418,14409,14401,14392,14384,14375,14367,14358,14350,14341,14332,14324,14315,14307,14298,14290,14281,14273,14264,14256,14247, +14239,14230,14222,14213,14205,14196,14188,14179,14171,14162,14154,14145,14137,14128,14120,14111,14103,14094,14086,14077,14069,14060,14052,14043,14035,14026,14018,14009,14001,13992,13984,13976,13967,13959,13950,13942,13933,13925,13916,13908,13899,13891,13882,13874,13865,13857,13848,13840,13831,13823, +13814,13806,13797,13789,13781,13772,13764,13755,13747,13738,13730,13721,13713,13704,13696,13687,13679,13670,13662,13654,13645,13637,13628,13620,13611,13603,13594,13586,13577,13569,13561,13552,13544,13535,13527,13518,13510,13501,13493,13485,13476,13468,13459,13451,13442,13434,13425,13417,13409,13400, +13392,13383,13375,13366,13358,13350,13341,13333,13324,13316,13307,13299,13291,13282,13274,13265,13257,13248,13240,13232,13223,13215,13206,13198,13189,13181,13173,13164,13156,13147,13139,13131,13122,13114,13105,13097,13089,13080,13072,13063,13055,13047,13038,13030,13021,13013,13005,12996,12988,12979, +12971,12963,12954,12946,12937,12929,12921,12912,12904,12896,12887,12879,12870,12862,12854,12845,12837,12828,12820,12812,12803,12795,12787,12778,12770,12762,12753,12745,12736,12728,12720,12711,12703,12695,12686,12678,12670,12661,12653,12644,12636,12628,12619,12611,12603,12594,12586,12578,12569,12561, +12553,12544,12536,12528,12519,12511,12503,12494,12486,12478,12469,12461,12453,12444,12436,12428,12419,12411,12403,12394,12386,12378,12369,12361,12353,12344,12336,12328,12320,12311,12303,12295,12286,12278,12270,12261,12253,12245,12236,12228,12220,12212,12203,12195,12187,12178,12170,12162,12154,12145, +12137,12129,12120,12112,12104,12096,12087,12079,12071,12062,12054,12046,12038,12029,12021,12013,12005,11996,11988,11980,11971,11963,11955,11947,11938,11930,11922,11914,11905,11897,11889,11881,11872,11864,11856,11848,11839,11831,11823,11815,11806,11798,11790,11782,11774,11765,11757,11749,11741,11732, +11724,11716,11708,11699,11691,11683,11675,11667,11658,11650,11642,11634,11626,11617,11609,11601,11593,11585,11576,11568,11560,11552,11544,11535,11527,11519,11511,11503,11494,11486,11478,11470,11462,11453,11445,11437,11429,11421,11413,11404,11396,11388,11380,11372,11364,11355,11347,11339,11331,11323, +11315,11306,11298,11290,11282,11274,11266,11257,11249,11241,11233,11225,11217,11209,11200,11192,11184,11176,11168,11160,11152,11144,11135,11127,11119,11111,11103,11095,11087,11079,11070,11062,11054,11046,11038,11030,11022,11014,11006,10997,10989,10981,10973,10965,10957,10949,10941,10933,10925,10916, +10908,10900,10892,10884,10876,10868,10860,10852,10844,10836,10828,10820,10811,10803,10795,10787,10779,10771,10763,10755,10747,10739,10731,10723,10715,10707,10699,10691,10683,10675,10667,10658,10650,10642,10634,10626,10618,10610,10602,10594,10586,10578,10570,10562,10554,10546,10538,10530,10522,10514, +10506,10498,10490,10482,10474,10466,10458,10450,10442,10434,10426,10418,10410,10402,10394,10386,10378,10370,10362,10354,10346,10338,10330,10322,10314,10306,10298,10290,10282,10274,10267,10259,10251,10243,10235,10227,10219,10211,10203,10195,10187,10179,10171,10163,10155,10147,10139,10131,10124,10116, +10108,10100,10092,10084,10076,10068,10060,10052,10044,10036,10029,10021,10013,10005,9997,9989,9981,9973,9965,9957,9950,9942,9934,9926,9918,9910,9902,9894,9886,9879,9871,9863,9855,9847,9839,9831,9824,9816,9808,9800,9792,9784,9776,9769,9761,9753,9745,9737,9729,9721, +9714,9706,9698,9690,9682,9674,9667,9659,9651,9643,9635,9628,9620,9612,9604,9596,9588,9581,9573,9565,9557,9549,9542,9534,9526,9518,9511,9503,9495,9487,9479,9472,9464,9456,9448,9440,9433,9425,9417,9409,9402,9394,9386,9378,9371,9363,9355,9347,9340,9332, +9324,9316,9309,9301,9293,9285,9278,9270,9262,9255,9247,9239,9231,9224,9216,9208,9201,9193,9185,9177,9170,9162,9154,9147,9139,9131,9124,9116,9108,9100,9093,9085,9077,9070,9062,9054,9047,9039,9031,9024,9016,9008,9001,8993,8985,8978,8970,8962,8955,8947, +8940,8932,8924,8917,8909,8901,8894,8886,8878,8871,8863,8856,8848,8840,8833,8825,8817,8810,8802,8795,8787,8779,8772,8764,8757,8749,8742,8734,8726,8719,8711,8704,8696,8688,8681,8673,8666,8658,8651,8643,8635,8628,8620,8613,8605,8598,8590,8583,8575,8568, +8560,8552,8545,8537,8530,8522,8515,8507,8500,8492,8485,8477,8470,8462,8455,8447,8440,8432,8425,8417,8410,8402,8395,8387,8380,8372,8365,8357,8350,8342,8335,8327,8320,8312,8305,8297,8290,8283,8275,8268,8260,8253,8245,8238,8230,8223,8216,8208,8201,8193, +8186,8178,8171,8164,8156,8149,8141,8134,8126,8119,8112,8104,8097,8089,8082,8075,8067,8060,8052,8045,8038,8030,8023,8016,8008,8001,7993,7986,7979,7971,7964,7957,7949,7942,7935,7927,7920,7913,7905,7898,7891,7883,7876,7869,7861,7854,7847,7839,7832,7825, +7817,7810,7803,7795,7788,7781,7773,7766,7759,7752,7744,7737,7730,7722,7715,7708,7701,7693,7686,7679,7671,7664,7657,7650,7642,7635,7628,7621,7613,7606,7599,7592,7584,7577,7570,7563,7556,7548,7541,7534,7527,7519,7512,7505,7498,7491,7483,7476,7469,7462, +7455,7447,7440,7433,7426,7419,7411,7404,7397,7390,7383,7376,7368,7361,7354,7347,7340,7333,7326,7318,7311,7304,7297,7290,7283,7276,7268,7261,7254,7247,7240,7233,7226,7219,7211,7204,7197,7190,7183,7176,7169,7162,7155,7148,7140,7133,7126,7119,7112,7105, +7098,7091,7084,7077,7070,7063,7056,7049,7042,7035,7027,7020,7013,7006,6999,6992,6985,6978,6971,6964,6957,6950,6943,6936,6929,6922,6915,6908,6901,6894,6887,6880,6873,6866,6859,6852,6845,6838,6831,6824,6817,6810,6803,6796,6790,6783,6776,6769,6762,6755, +6748,6741,6734,6727,6720,6713,6706,6699,6692,6685,6679,6672,6665,6658,6651,6644,6637,6630,6623,6616,6610,6603,6596,6589,6582,6575,6568,6561,6555,6548,6541,6534,6527,6520,6513,6507,6500,6493,6486,6479,6472,6466,6459,6452,6445,6438,6431,6425,6418,6411, +6404,6397,6391,6384,6377,6370,6363,6357,6350,6343,6336,6330,6323,6316,6309,6302,6296,6289,6282,6275,6269,6262,6255,6248,6242,6235,6228,6222,6215,6208,6201,6195,6188,6181,6174,6168,6161,6154,6148,6141,6134,6128,6121,6114,6108,6101,6094,6088,6081,6074, +6067,6061,6054,6048,6041,6034,6028,6021,6014,6008,6001,5994,5988,5981,5974,5968,5961,5955,5948,5941,5935,5928,5922,5915,5908,5902,5895,5889,5882,5875,5869,5862,5856,5849,5843,5836,5829,5823,5816,5810,5803,5797,5790,5784,5777,5770,5764,5757,5751,5744, +5738,5731,5725,5718,5712,5705,5699,5692,5686,5679,5673,5666,5660,5653,5647,5640,5634,5627,5621,5614,5608,5602,5595,5589,5582,5576,5569,5563,5556,5550,5544,5537,5531,5524,5518,5511,5505,5499,5492,5486,5479,5473,5467,5460,5454,5447,5441,5435,5428,5422, +5415,5409,5403,5396,5390,5384,5377,5371,5365,5358,5352,5346,5339,5333,5327,5320,5314,5308,5301,5295,5289,5282,5276,5270,5263,5257,5251,5244,5238,5232,5226,5219,5213,5207,5201,5194,5188,5182,5175,5169,5163,5157,5150,5144,5138,5132,5125,5119,5113,5107, +5101,5094,5088,5082,5076,5070,5063,5057,5051,5045,5039,5032,5026,5020,5014,5008,5001,4995,4989,4983,4977,4971,4964,4958,4952,4946,4940,4934,4928,4921,4915,4909,4903,4897,4891,4885,4879,4873,4866,4860,4854,4848,4842,4836,4830,4824,4818,4812,4806,4800, +4793,4787,4781,4775,4769,4763,4757,4751,4745,4739,4733,4727,4721,4715,4709,4703,4697,4691,4685,4679,4673,4667,4661,4655,4649,4643,4637,4631,4625,4619,4613,4607,4601,4595,4589,4583,4577,4571,4565,4559,4553,4548,4542,4536,4530,4524,4518,4512,4506,4500, +4494,4488,4482,4477,4471,4465,4459,4453,4447,4441,4435,4430,4424,4418,4412,4406,4400,4394,4389,4383,4377,4371,4365,4359,4354,4348,4342,4336,4330,4324,4319,4313,4307,4301,4295,4290,4284,4278,4272,4267,4261,4255,4249,4243,4238,4232,4226,4220,4215,4209, +4203,4198,4192,4186,4180,4175,4169,4163,4157,4152,4146,4140,4135,4129,4123,4118,4112,4106,4101,4095,4089,4083,4078,4072,4067,4061,4055,4050,4044,4038,4033,4027,4021,4016,4010,4004,3999,3993,3988,3982,3976,3971,3965,3960,3954,3948,3943,3937,3932,3926, +3921,3915,3909,3904,3898,3893,3887,3882,3876,3871,3865,3860,3854,3848,3843,3837,3832,3826,3821,3815,3810,3804,3799,3793,3788,3782,3777,3771,3766,3761,3755,3750,3744,3739,3733,3728,3722,3717,3711,3706,3701,3695,3690,3684,3679,3673,3668,3663,3657,3652, +3646,3641,3636,3630,3625,3619,3614,3609,3603,3598,3593,3587,3582,3577,3571,3566,3561,3555,3550,3545,3539,3534,3529,3523,3518,3513,3507,3502,3497,3491,3486,3481,3476,3470,3465,3460,3454,3449,3444,3439,3433,3428,3423,3418,3412,3407,3402,3397,3391,3386, +3381,3376,3371,3365,3360,3355,3350,3345,3339,3334,3329,3324,3319,3313,3308,3303,3298,3293,3288,3282,3277,3272,3267,3262,3257,3252,3247,3241,3236,3231,3226,3221,3216,3211,3206,3201,3195,3190,3185,3180,3175,3170,3165,3160,3155,3150,3145,3140,3135,3130, +3125,3119,3114,3109,3104,3099,3094,3089,3084,3079,3074,3069,3064,3059,3054,3049,3044,3039,3034,3029,3024,3020,3015,3010,3005,3000,2995,2990,2985,2980,2975,2970,2965,2960,2955,2950,2945,2941,2936,2931,2926,2921,2916,2911,2906,2901,2897,2892,2887,2882, +2877,2872,2867,2863,2858,2853,2848,2843,2838,2834,2829,2824,2819,2814,2810,2805,2800,2795,2790,2786,2781,2776,2771,2766,2762,2757,2752,2747,2743,2738,2733,2728,2724,2719,2714,2709,2705,2700,2695,2691,2686,2681,2676,2672,2667,2662,2658,2653,2648,2644, +2639,2634,2630,2625,2620,2616,2611,2606,2602,2597,2592,2588,2583,2579,2574,2569,2565,2560,2556,2551,2546,2542,2537,2533,2528,2523,2519,2514,2510,2505,2501,2496,2492,2487,2482,2478,2473,2469,2464,2460,2455,2451,2446,2442,2437,2433,2428,2424,2419,2415, +2410,2406,2401,2397,2392,2388,2383,2379,2375,2370,2366,2361,2357,2352,2348,2343,2339,2335,2330,2326,2321,2317,2313,2308,2304,2299,2295,2291,2286,2282,2278,2273,2269,2265,2260,2256,2251,2247,2243,2238,2234,2230,2226,2221,2217,2213,2208,2204,2200,2195, +2191,2187,2183,2178,2174,2170,2165,2161,2157,2153,2148,2144,2140,2136,2131,2127,2123,2119,2115,2110,2106,2102,2098,2094,2089,2085,2081,2077,2073,2068,2064,2060,2056,2052,2048,2043,2039,2035,2031,2027,2023,2019,2015,2010,2006,2002,1998,1994,1990,1986, +1982,1978,1974,1969,1965,1961,1957,1953,1949,1945,1941,1937,1933,1929,1925,1921,1917,1913,1909,1905,1901,1897,1893,1889,1885,1881,1877,1873,1869,1865,1861,1857,1853,1849,1845,1841,1837,1833,1829,1825,1821,1817,1813,1809,1806,1802,1798,1794,1790,1786, +1782,1778,1774,1770,1767,1763,1759,1755,1751,1747,1743,1740,1736,1732,1728,1724,1720,1717,1713,1709,1705,1701,1698,1694,1690,1686,1682,1679,1675,1671,1667,1663,1660,1656,1652,1648,1645,1641,1637,1633,1630,1626,1622,1619,1615,1611,1607,1604,1600,1596, +1593,1589,1585,1582,1578,1574,1571,1567,1563,1560,1556,1552,1549,1545,1541,1538,1534,1530,1527,1523,1520,1516,1512,1509,1505,1502,1498,1494,1491,1487,1484,1480,1477,1473,1470,1466,1462,1459,1455,1452,1448,1445,1441,1438,1434,1431,1427,1424,1420,1417, +1413,1410,1406,1403,1399,1396,1392,1389,1385,1382,1379,1375,1372,1368,1365,1361,1358,1355,1351,1348,1344,1341,1338,1334,1331,1327,1324,1321,1317,1314,1310,1307,1304,1300,1297,1294,1290,1287,1284,1280,1277,1274,1270,1267,1264,1261,1257,1254,1251,1247, +1244,1241,1238,1234,1231,1228,1224,1221,1218,1215,1211,1208,1205,1202,1199,1195,1192,1189,1186,1183,1179,1176,1173,1170,1167,1163,1160,1157,1154,1151,1148,1144,1141,1138,1135,1132,1129,1126,1122,1119,1116,1113,1110,1107,1104,1101,1098,1095,1091,1088, +1085,1082,1079,1076,1073,1070,1067,1064,1061,1058,1055,1052,1049,1046,1043,1040,1037,1034,1031,1028,1025,1022,1019,1016,1013,1010,1007,1004,1001,998,995,992,989,986,983,980,978,975,972,969,966,963,960,957,954,951,949,946,943,940, +937,934,931,928,926,923,920,917,914,911,909,906,903,900,897,895,892,889,886,883,881,878,875,872,870,867,864,861,859,856,853,850,848,845,842,840,837,834,831,829,826,823,821,818,815,813,810,807,805,802, +799,797,794,791,789,786,784,781,778,776,773,771,768,765,763,760,758,755,752,750,747,745,742,740,737,735,732,729,727,724,722,719,717,714,712,709,707,704,702,699,697,694,692,690,687,685,682,680,677,675, +672,670,668,665,663,660,658,655,653,651,648,646,643,641,639,636,634,632,629,627,625,622,620,618,615,613,611,608,606,604,601,599,597,594,592,590,588,585,583,581,579,576,574,572,570,567,565,563,561,558, +556,554,552,550,547,545,543,541,539,536,534,532,530,528,526,523,521,519,517,515,513,511,508,506,504,502,500,498,496,494,492,490,487,485,483,481,479,477,475,473,471,469,467,465,463,461,459,457,455,453, +451,449,447,445,443,441,439,437,435,433,431,429,427,425,423,421,419,417,415,414,412,410,408,406,404,402,400,398,397,395,393,391,389,387,385,383,382,380,378,376,374,373,371,369,367,365,363,362,360,358, +356,355,353,351,349,347,346,344,342,340,339,337,335,334,332,330,328,327,325,323,322,320,318,317,315,313,312,310,308,307,305,303,302,300,298,297,295,293,292,290,289,287,285,284,282,281,279,278,276,274, +273,271,270,268,267,265,264,262,261,259,257,256,254,253,251,250,248,247,245,244,243,241,240,238,237,235,234,232,231,229,228,227,225,224,222,221,220,218,217,215,214,213,211,210,209,207,206,204,203,202, +200,199,198,196,195,194,192,191,190,189,187,186,185,183,182,181,180,178,177,176,175,173,172,171,170,168,167,166,165,163,162,161,160,159,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140, +139,138,137,136,135,134,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,92,91,90, +89,88,87,86,85,84,84,83,82,81,80,79,78,78,77,76,75,74,74,73,72,71,70,70,69,68,67,66,66,65,64,63,63,62,61,60,60,59,58,57,57,56,55,55,54,53,53,52,51,51, +50,49,49,48,47,47,46,45,45,44,43,43,42,42,41,40,40,39,39,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,28,27,27,26,26,25,25,24,24,23,23,22, +22,22,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6, +5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +#endif \ No newline at end of file From a765e863c08d225fa07a32018e69e1dded336d58 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 19 Apr 2020 20:43:50 +0200 Subject: [PATCH 042/119] delete old BS --- CTCSSDecoder.cpp | 46 ------------------- CTCSSDecoder.h | 112 ----------------------------------------------- Goertzel.cpp | 82 ---------------------------------- Goertzel.h | 66 ---------------------------- 4 files changed, 306 deletions(-) delete mode 100644 CTCSSDecoder.cpp delete mode 100644 CTCSSDecoder.h delete mode 100644 Goertzel.cpp delete mode 100644 Goertzel.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp deleted file mode 100644 index 8905b2b..0000000 --- a/CTCSSDecoder.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "CTCSSDecoder.h" - -CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : -m_thresholdSquared(threshold * threshold), -m_hasCTCSS(false) -{ - m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); -} - - -void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) -{ - unsigned int f1MagSquared, f2MagSquared, f3MagSquared; - GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); - - if(result == GR_READY) { - // Goertzel says it has something for us, so checkt it - // make sure the strongest tone is the wanted one and not the neighbouring tone - m_hasCTCSS = f2MagSquared >= m_thresholdSquared - && f2MagSquared > f1MagSquared - && f2MagSquared > f3MagSquared; - } -} - -bool CCTCSSDEcoder::hasCTCSS() -{ - return m_hasCTCSS; -} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h deleted file mode 100644 index cf9e5e4..0000000 --- a/CTCSSDecoder.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(CTCSSDECODER_H) -#define CTCSSDECODER_H - -#include "Globals.h" -#include "Goertzel.h" -#include "HannWindow.h" - -#define N_SAMPLES 12000 - -typedef struct -{ - TGoertzelParameters toneLow, tone, toneHi; -} TCTCSSTone; - - -/* - * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. - * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. - * We need in since intermediate values in goertzel will overflow Q15 - */ -const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; -const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; -const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; -const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; -const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; -const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; -const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; -const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; -const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; -const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; -const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; -const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; -const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; -const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; -const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; -const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; -const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; -const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; -const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; -const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; -const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; -const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; -const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; -const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; -const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; -const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; -const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; -const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; -const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; -const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; -const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; -const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; -const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; -const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; -const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; -const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; -const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; -const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; -const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; -const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; -const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; -const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; -const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; -const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; -const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; -const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; -const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; -const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; -const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; -const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; -const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; -const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; -const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; -const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; -const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; - - - -class CCTCSSDEcoder { -public: - //threshold must be 0.0 to 1.0; - CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); - - void samples(const q15_t* samples, uint8_t length); - bool hasCTCSS(); - -private: - unsigned int m_thresholdSquared; - bool m_hasCTCSS; - CGoertzel* m_goertzel; - -}; - -#endif diff --git a/Goertzel.cpp b/Goertzel.cpp deleted file mode 100644 index 811fd97..0000000 --- a/Goertzel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "Goertzel.h" - -CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : -m_min(0), -m_max(0), -m_processedSamplesCount(0), -m_n(n), -m_window(window),//Window should not be deleted by someone else -m_windowCorr(windowCorr) -{ - m_freqs[0] = f1; - m_freqs[1] = f2; - m_freqs[2] = f3; - - reset(); -} - -void CGoertzel::reset() -{ - ::memset(m_q1s, 0, sizeof(m_q1s)); - ::memset(m_q2s, 0, sizeof(m_q2s)); - m_processedSamplesCount = 0U; - m_max = 0U; - m_min = 0U; -} - -GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) -{ - int scalingFactor = (length / 2) * m_windowCorr; - unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; - GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; - - for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { - - if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; - if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; - - for(uint8_t i = 0; i < 3; i++) { - int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); - m_q2s[i] = m_q1s[i]; - m_q1s[i] = q0; - } - - m_processedSamplesCount++; - //we have collected enough samples, evaluate now, - if(m_processedSamplesCount == m_n) { - if(magnitudesComputed == GR_NOT_READY) { - //however if we already had collected enough samples only keep the magnitudes we computed the first time - int span = m_max - m_min; - for(uint8_t i = 0; i < 3; i++) { - int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic - int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; - - *(magnitudes[i]) = real * real + imag * imag; - } - - magnitudesComputed = GR_READY; - } - reset(); - } - } - - return magnitudesComputed; -} diff --git a/Goertzel.h b/Goertzel.h deleted file mode 100644 index 1b15db9..0000000 --- a/Goertzel.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(GOERTZEL_H) -#define GOERTZEL_H - -#include "Globals.h" - -typedef struct -{ - int sin, cos, coeff; -} TGoertzelParameters; - - -enum GOERTZEL_RESULT : uint8_t -{ - GR_NOT_READY = 0, - GR_READY = 1 -}; - -class CGoertzel { -public: - //f1,f2,f3 are natural frequencies - CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); - - void reset(); - GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); - -private: - TGoertzelParameters m_freqs[3]; - - q15_t m_min; - q15_t m_max; - - q15_t m_omegas[3]; - int m_sines[3]; - int m_cosines[3]; - int m_coeffs[3]; - - int m_q1s[3]; - int m_q2s[3]; - - uint16_t m_processedSamplesCount; - uint16_t m_n; - - const int* m_window; - int m_windowCorr; -}; - -#endif From 94f566aacccffaecabc263627ea576e772bd4b06 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 13 Apr 2020 16:15:24 +0200 Subject: [PATCH 043/119] Add Goertzel --- Goertzel.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Goertzel.h | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 Goertzel.cpp create mode 100644 Goertzel.h diff --git a/Goertzel.cpp b/Goertzel.cpp new file mode 100644 index 0000000..811fd97 --- /dev/null +++ b/Goertzel.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "Goertzel.h" + +CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : +m_min(0), +m_max(0), +m_processedSamplesCount(0), +m_n(n), +m_window(window),//Window should not be deleted by someone else +m_windowCorr(windowCorr) +{ + m_freqs[0] = f1; + m_freqs[1] = f2; + m_freqs[2] = f3; + + reset(); +} + +void CGoertzel::reset() +{ + ::memset(m_q1s, 0, sizeof(m_q1s)); + ::memset(m_q2s, 0, sizeof(m_q2s)); + m_processedSamplesCount = 0U; + m_max = 0U; + m_min = 0U; +} + +GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) +{ + int scalingFactor = (length / 2) * m_windowCorr; + unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; + GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; + + for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { + + if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; + if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; + + for(uint8_t i = 0; i < 3; i++) { + int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); + m_q2s[i] = m_q1s[i]; + m_q1s[i] = q0; + } + + m_processedSamplesCount++; + //we have collected enough samples, evaluate now, + if(m_processedSamplesCount == m_n) { + if(magnitudesComputed == GR_NOT_READY) { + //however if we already had collected enough samples only keep the magnitudes we computed the first time + int span = m_max - m_min; + for(uint8_t i = 0; i < 3; i++) { + int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic + int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; + + *(magnitudes[i]) = real * real + imag * imag; + } + + magnitudesComputed = GR_READY; + } + reset(); + } + } + + return magnitudesComputed; +} diff --git a/Goertzel.h b/Goertzel.h new file mode 100644 index 0000000..1b15db9 --- /dev/null +++ b/Goertzel.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(GOERTZEL_H) +#define GOERTZEL_H + +#include "Globals.h" + +typedef struct +{ + int sin, cos, coeff; +} TGoertzelParameters; + + +enum GOERTZEL_RESULT : uint8_t +{ + GR_NOT_READY = 0, + GR_READY = 1 +}; + +class CGoertzel { +public: + //f1,f2,f3 are natural frequencies + CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); + + void reset(); + GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); + +private: + TGoertzelParameters m_freqs[3]; + + q15_t m_min; + q15_t m_max; + + q15_t m_omegas[3]; + int m_sines[3]; + int m_cosines[3]; + int m_coeffs[3]; + + int m_q1s[3]; + int m_q2s[3]; + + uint16_t m_processedSamplesCount; + uint16_t m_n; + + const int* m_window; + int m_windowCorr; +}; + +#endif From 3d54519dbf58d4b085fefdf6a5d1132984b61226 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 14 Apr 2020 12:16:07 +0200 Subject: [PATCH 044/119] add CTCSS decoder --- CTCSSDecoder.cpp | 46 +++++++++++++++++++ CTCSSDecoder.h | 112 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 CTCSSDecoder.cpp create mode 100644 CTCSSDecoder.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp new file mode 100644 index 0000000..8905b2b --- /dev/null +++ b/CTCSSDecoder.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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 "CTCSSDecoder.h" + +CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : +m_thresholdSquared(threshold * threshold), +m_hasCTCSS(false) +{ + m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); +} + + +void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) +{ + unsigned int f1MagSquared, f2MagSquared, f3MagSquared; + GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); + + if(result == GR_READY) { + // Goertzel says it has something for us, so checkt it + // make sure the strongest tone is the wanted one and not the neighbouring tone + m_hasCTCSS = f2MagSquared >= m_thresholdSquared + && f2MagSquared > f1MagSquared + && f2MagSquared > f3MagSquared; + } +} + +bool CCTCSSDEcoder::hasCTCSS() +{ + return m_hasCTCSS; +} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h new file mode 100644 index 0000000..cf9e5e4 --- /dev/null +++ b/CTCSSDecoder.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA + * + * 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(CTCSSDECODER_H) +#define CTCSSDECODER_H + +#include "Globals.h" +#include "Goertzel.h" +#include "HannWindow.h" + +#define N_SAMPLES 12000 + +typedef struct +{ + TGoertzelParameters toneLow, tone, toneHi; +} TCTCSSTone; + + +/* + * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. + * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. + * We need in since intermediate values in goertzel will overflow Q15 + */ +const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; +const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; +const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; +const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; +const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; +const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; +const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; +const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; +const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; +const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; +const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; +const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; +const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; +const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; +const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; +const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; +const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; +const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; +const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; +const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; +const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; +const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; +const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; +const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; +const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; +const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; +const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; +const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; +const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; +const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; +const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; +const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; +const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; +const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; +const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; +const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; +const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; +const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; +const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; +const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; +const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; +const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; +const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; +const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; +const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; +const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; +const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; +const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; +const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; +const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; +const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; +const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; +const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; +const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; +const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; + + + +class CCTCSSDEcoder { +public: + //threshold must be 0.0 to 1.0; + CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); + + void samples(const q15_t* samples, uint8_t length); + bool hasCTCSS(); + +private: + unsigned int m_thresholdSquared; + bool m_hasCTCSS; + CGoertzel* m_goertzel; + +}; + +#endif From 939b019592c96e693c238877a8f9ae09c71296e5 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 19 Apr 2020 20:45:14 +0200 Subject: [PATCH 045/119] delete old BS --- CTCSSDecoder.cpp | 46 ------------------- CTCSSDecoder.h | 112 ----------------------------------------------- Goertzel.cpp | 82 ---------------------------------- Goertzel.h | 66 ---------------------------- 4 files changed, 306 deletions(-) delete mode 100644 CTCSSDecoder.cpp delete mode 100644 CTCSSDecoder.h delete mode 100644 Goertzel.cpp delete mode 100644 Goertzel.h diff --git a/CTCSSDecoder.cpp b/CTCSSDecoder.cpp deleted file mode 100644 index 8905b2b..0000000 --- a/CTCSSDecoder.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "CTCSSDecoder.h" - -CCTCSSDEcoder::CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold) : -m_thresholdSquared(threshold * threshold), -m_hasCTCSS(false) -{ - m_goertzel = new CGoertzel(tone.toneLow, tone.tone, tone.toneHi, HANN_WINDOW, HANN_WINDOW_CORR, N_SAMPLES); -} - - -void CCTCSSDEcoder::samples(const q15_t* samples, uint8_t length) -{ - unsigned int f1MagSquared, f2MagSquared, f3MagSquared; - GOERTZEL_RESULT result = m_goertzel->samples(samples, length, &f1MagSquared, &f2MagSquared, &f3MagSquared); - - if(result == GR_READY) { - // Goertzel says it has something for us, so checkt it - // make sure the strongest tone is the wanted one and not the neighbouring tone - m_hasCTCSS = f2MagSquared >= m_thresholdSquared - && f2MagSquared > f1MagSquared - && f2MagSquared > f3MagSquared; - } -} - -bool CCTCSSDEcoder::hasCTCSS() -{ - return m_hasCTCSS; -} diff --git a/CTCSSDecoder.h b/CTCSSDecoder.h deleted file mode 100644 index cf9e5e4..0000000 --- a/CTCSSDecoder.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(CTCSSDECODER_H) -#define CTCSSDECODER_H - -#include "Globals.h" -#include "Goertzel.h" -#include "HannWindow.h" - -#define N_SAMPLES 12000 - -typedef struct -{ - TGoertzelParameters toneLow, tone, toneHi; -} TCTCSSTone; - - -/* - * Those are precalculated values (sin, cos, coeff) for ech tone, tone - 2Hz, tone + 2Hz. - * Those are Q15 values but stored in int. This to avoid unnecessary cast during Goertzel calculation. - * We need in since intermediate values in goertzel will overflow Q15 - */ -const TCTCSSTone TONE_067_0 {{558, 32763, 65527}, {575, 32763, 65526}, {592, 32763, 65525}}; -const TCTCSSTone TONE_069_3 {{577, 32763, 65526}, {594, 32763, 65525}, {612, 32762, 65525}}; -const TCTCSSTone TONE_071_9 {{600, 32763, 65525}, {617, 32762, 65524}, {634, 32762, 65524}}; -const TCTCSSTone TONE_074_4 {{621, 32762, 65524}, {638, 32762, 65524}, {655, 32761, 65523}}; -const TCTCSSTone TONE_077_0 {{643, 32762, 65523}, {661, 32761, 65523}, {678, 32761, 65522}}; -const TCTCSSTone TONE_079_7 {{667, 32761, 65522}, {684, 32761, 65522}, {701, 32761, 65521}}; -const TCTCSSTone TONE_082_5 {{691, 32761, 65521}, {708, 32760, 65521}, {725, 32760, 65520}}; -const TCTCSSTone TONE_085_4 {{715, 32760, 65520}, {733, 32760, 65520}, {750, 32759, 65519}}; -const TCTCSSTone TONE_088_5 {{742, 32760, 65519}, {759, 32759, 65518}, {776, 32759, 65518}}; -const TCTCSSTone TONE_091_5 {{768, 32759, 65518}, {785, 32759, 65517}, {802, 32758, 65516}}; -const TCTCSSTone TONE_094_8 {{796, 32758, 65517}, {813, 32758, 65516}, {830, 32757, 65515}}; -const TCTCSSTone TONE_097_4 {{818, 32758, 65516}, {835, 32757, 65515}, {853, 32757, 65514}}; -const TCTCSSTone TONE_100_0 {{841, 32757, 65514}, {858, 32757, 65514}, {875, 32756, 65513}}; -const TCTCSSTone TONE_103_5 {{871, 32756, 65513}, {888, 32756, 65512}, {905, 32756, 65511}}; -const TCTCSSTone TONE_107_2 {{902, 32756, 65511}, {920, 32755, 65510}, {937, 32755, 65509}}; -const TCTCSSTone TONE_110_9 {{934, 32755, 65509}, {951, 32754, 65508}, {968, 32754, 65507}}; -const TCTCSSTone TONE_114_8 {{968, 32754, 65507}, {985, 32753, 65506}, {1002, 32753, 65505}}; -const TCTCSSTone TONE_123_0 {{1038, 32752, 65503}, {1055, 32751, 65502}, {1072, 32750, 65501}}; -const TCTCSSTone TONE_127_3 {{1075, 32750, 65501}, {1092, 32750, 65500}, {1109, 32749, 65498}}; -const TCTCSSTone TONE_131_8 {{1113, 32749, 65498}, {1130, 32748, 65497}, {1148, 32748, 65496}}; -const TCTCSSTone TONE_136_5 {{1154, 32748, 65495}, {1171, 32747, 65494}, {1188, 32746, 65493}}; -const TCTCSSTone TONE_141_3 {{1195, 32746, 65492}, {1212, 32746, 65491}, {1229, 32745, 65490}}; -const TCTCSSTone TONE_146_2 {{1237, 32745, 65489}, {1254, 32744, 65488}, {1271, 32743, 65487}}; -const TCTCSSTone TONE_150_0 {{1269, 32743, 65487}, {1286, 32743, 65485}, {1304, 32742, 65484}}; -const TCTCSSTone TONE_151_4 {{1281, 32743, 65486}, {1298, 32742, 65485}, {1316, 32742, 65483}}; -const TCTCSSTone TONE_156_7 {{1327, 32741, 65482}, {1344, 32740, 65481}, {1361, 32740, 65479}}; -const TCTCSSTone TONE_159_8 {{1353, 32740, 65480}, {1370, 32739, 65479}, {1388, 32739, 65477}}; -const TCTCSSTone TONE_162_2 {{1374, 32739, 65478}, {1391, 32738, 65477}, {1408, 32738, 65475}}; -const TCTCSSTone TONE_165_5 {{1402, 32738, 65476}, {1419, 32737, 65474}, {1436, 32736, 65473}}; -const TCTCSSTone TONE_167_9 {{1423, 32737, 65474}, {1440, 32736, 65473}, {1457, 32736, 65471}}; -const TCTCSSTone TONE_171_3 {{1452, 32736, 65472}, {1469, 32735, 65470}, {1486, 32734, 65469}}; -const TCTCSSTone TONE_173_8 {{1473, 32735, 65470}, {1490, 32734, 65468}, {1508, 32733, 65467}}; -const TCTCSSTone TONE_177_3 {{1503, 32733, 65467}, {1520, 32733, 65465}, {1538, 32732, 65464}}; -const TCTCSSTone TONE_179_9 {{1526, 32732, 65465}, {1543, 32732, 65463}, {1560, 32731, 65462}}; -const TCTCSSTone TONE_183_5 {{1556, 32731, 65462}, {1574, 32730, 65460}, {1591, 32729, 65459}}; -const TCTCSSTone TONE_186_2 {{1580, 32730, 65460}, {1597, 32729, 65458}, {1614, 32728, 65456}}; -const TCTCSSTone TONE_188_8 {{1602, 32729, 65458}, {1619, 32728, 65456}, {1636, 32727, 65454}}; -const TCTCSSTone TONE_189_9 {{1611, 32728, 65457}, {1628, 32728, 65455}, {1646, 32727, 65453}}; -const TCTCSSTone TONE_192_8 {{1636, 32727, 65454}, {1653, 32726, 65453}, {1670, 32725, 65451}}; -const TCTCSSTone TONE_196_6 {{1669, 32725, 65451}, {1686, 32725, 65449}, {1703, 32724, 65447}}; -const TCTCSSTone TONE_199_5 {{1694, 32724, 65448}, {1711, 32723, 65447}, {1728, 32722, 65445}}; -const TCTCSSTone TONE_203_5 {{1728, 32722, 65445}, {1745, 32722, 65443}, {1762, 32721, 65441}}; -const TCTCSSTone TONE_206_5 {{1753, 32721, 65442}, {1771, 32720, 65440}, {1788, 32719, 65438}}; -const TCTCSSTone TONE_210_7 {{1789, 32719, 65438}, {1807, 32718, 65436}, {1824, 32717, 65434}}; -const TCTCSSTone TONE_213_8 {{1816, 32718, 65435}, {1833, 32717, 65433}, {1850, 32716, 65431}}; -const TCTCSSTone TONE_218_1 {{1853, 32716, 65431}, {1870, 32715, 65429}, {1887, 32714, 65427}}; -const TCTCSSTone TONE_221_3 {{1880, 32714, 65428}, {1897, 32713, 65426}, {1915, 32712, 65424}}; -const TCTCSSTone TONE_225_7 {{1918, 32712, 65424}, {1935, 32711, 65422}, {1952, 32710, 65420}}; -const TCTCSSTone TONE_229_1 {{1947, 32710, 65420}, {1964, 32709, 65418}, {1981, 32708, 65416}}; -const TCTCSSTone TONE_233_6 {{1986, 32708, 65416}, {2003, 32707, 65413}, {2020, 32706, 65411}}; -const TCTCSSTone TONE_237_1 {{2016, 32706, 65412}, {2033, 32705, 65410}, {2050, 32704, 65408}}; -const TCTCSSTone TONE_241_8 {{2056, 32703, 65407}, {2073, 32702, 65405}, {2090, 32701, 65403}}; -const TCTCSSTone TONE_245_5 {{2087, 32701, 65403}, {2105, 32700, 65401}, {2122, 32699, 65398}}; -const TCTCSSTone TONE_250_3 {{2129, 32699, 65398}, {2146, 32698, 65395}, {2163, 32697, 65393}}; -const TCTCSSTone TONE_254_1 {{2161, 32697, 65393}, {2178, 32696, 65391}, {2195, 32694, 65389}}; - - - -class CCTCSSDEcoder { -public: - //threshold must be 0.0 to 1.0; - CCTCSSDEcoder(const TCTCSSTone& tone, q15_t threshold); - - void samples(const q15_t* samples, uint8_t length); - bool hasCTCSS(); - -private: - unsigned int m_thresholdSquared; - bool m_hasCTCSS; - CGoertzel* m_goertzel; - -}; - -#endif diff --git a/Goertzel.cpp b/Goertzel.cpp deleted file mode 100644 index 811fd97..0000000 --- a/Goertzel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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 "Goertzel.h" - -CGoertzel::CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n) : -m_min(0), -m_max(0), -m_processedSamplesCount(0), -m_n(n), -m_window(window),//Window should not be deleted by someone else -m_windowCorr(windowCorr) -{ - m_freqs[0] = f1; - m_freqs[1] = f2; - m_freqs[2] = f3; - - reset(); -} - -void CGoertzel::reset() -{ - ::memset(m_q1s, 0, sizeof(m_q1s)); - ::memset(m_q2s, 0, sizeof(m_q2s)); - m_processedSamplesCount = 0U; - m_max = 0U; - m_min = 0U; -} - -GOERTZEL_RESULT CGoertzel::samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared) -{ - int scalingFactor = (length / 2) * m_windowCorr; - unsigned int * magnitudes[3] = {f1MagSquared, f2MagSquared, f3MagSquared}; - GOERTZEL_RESULT magnitudesComputed = GR_NOT_READY; - - for(uint8_t sampleIdx = 0; sampleIdx < length; sampleIdx++) { - - if(samples[sampleIdx] < m_min) m_min = samples[sampleIdx]; - if(samples[sampleIdx] > m_max) m_max = samples[sampleIdx]; - - for(uint8_t i = 0; i < 3; i++) { - int q0 = m_freqs[i].coeff * m_q1s[i] - m_q2s[i] + (samples[sampleIdx] * m_window[m_processedSamplesCount]); - m_q2s[i] = m_q1s[i]; - m_q1s[i] = q0; - } - - m_processedSamplesCount++; - //we have collected enough samples, evaluate now, - if(m_processedSamplesCount == m_n) { - if(magnitudesComputed == GR_NOT_READY) { - //however if we already had collected enough samples only keep the magnitudes we computed the first time - int span = m_max - m_min; - for(uint8_t i = 0; i < 3; i++) { - int real = ((m_q1s[i] * m_freqs[i].cos - m_q2s[i]) / scalingFactor)/span;//we divide by max-min so that we are normalized in the range [0, 1], this way we are input signal levels agnostic - int imag = ((m_q1s[i] * m_freqs[i].sin) / scalingFactor)/span; - - *(magnitudes[i]) = real * real + imag * imag; - } - - magnitudesComputed = GR_READY; - } - reset(); - } - } - - return magnitudesComputed; -} diff --git a/Goertzel.h b/Goertzel.h deleted file mode 100644 index 1b15db9..0000000 --- a/Goertzel.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(GOERTZEL_H) -#define GOERTZEL_H - -#include "Globals.h" - -typedef struct -{ - int sin, cos, coeff; -} TGoertzelParameters; - - -enum GOERTZEL_RESULT : uint8_t -{ - GR_NOT_READY = 0, - GR_READY = 1 -}; - -class CGoertzel { -public: - //f1,f2,f3 are natural frequencies - CGoertzel(const TGoertzelParameters& f1, const TGoertzelParameters& f2, const TGoertzelParameters& f3, const int* window, int windowCorr, uint16_t n); - - void reset(); - GOERTZEL_RESULT samples(const q15_t *samples, uint8_t length, unsigned int * f1MagSquared, unsigned int * f2MagSquared, unsigned int* f3MagSquared); - -private: - TGoertzelParameters m_freqs[3]; - - q15_t m_min; - q15_t m_max; - - q15_t m_omegas[3]; - int m_sines[3]; - int m_cosines[3]; - int m_coeffs[3]; - - int m_q1s[3]; - int m_q2s[3]; - - uint16_t m_processedSamplesCount; - uint16_t m_n; - - const int* m_window; - int m_windowCorr; -}; - -#endif From e4c351d5d0a035112ab22a16ce9e84831d8dc523 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 20 Apr 2020 07:46:03 +0200 Subject: [PATCH 046/119] delete old BS --- HannWindow.h | 268 --------------------------------------------------- 1 file changed, 268 deletions(-) delete mode 100644 HannWindow.h diff --git a/HannWindow.h b/HannWindow.h deleted file mode 100644 index 4df9a5e..0000000 --- a/HannWindow.h +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2020 by Jonathan Naylor G4KLX / Geoffrey Merck F4FXL - KC3FRA - * - * 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(HANNWINDOW_H) -#define HANNWINDOW_H - -/* Hann window value as Q15 yet stored into int to avoid overflowing during calculation */ - -const int HANN_WINDOW_CORR = 16383; - -const int HANN_WINDOW[12000] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5, -6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,15,15,15,16,16,17,17,17,18,18,19,19,19,20,20,21,21,22,22, -22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,35,35,36,36,37,37,38,39,39,40,40,41,42,42,43,43,44,45,45,46,47,47,48,49,49,50, -51,51,52,53,53,54,55,55,56,57,57,58,59,60,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,74,74,75,76,77,78,78,79,80,81,82,83,84,84,85,86,87,88,89, -90,91,92,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,134,135,136,137,138,139, -140,141,142,144,145,146,147,148,149,150,152,153,154,155,156,157,159,160,161,162,163,165,166,167,168,170,171,172,173,175,176,177,178,180,181,182,183,185,186,187,189,190,191,192,194,195,196,198,199,200, -202,203,204,206,207,209,210,211,213,214,215,217,218,220,221,222,224,225,227,228,229,231,232,234,235,237,238,240,241,243,244,245,247,248,250,251,253,254,256,257,259,261,262,264,265,267,268,270,271,273, -274,276,278,279,281,282,284,285,287,289,290,292,293,295,297,298,300,302,303,305,307,308,310,312,313,315,317,318,320,322,323,325,327,328,330,332,334,335,337,339,340,342,344,346,347,349,351,353,355,356, -358,360,362,363,365,367,369,371,373,374,376,378,380,382,383,385,387,389,391,393,395,397,398,400,402,404,406,408,410,412,414,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451, -453,455,457,459,461,463,465,467,469,471,473,475,477,479,481,483,485,487,490,492,494,496,498,500,502,504,506,508,511,513,515,517,519,521,523,526,528,530,532,534,536,539,541,543,545,547,550,552,554,556, -558,561,563,565,567,570,572,574,576,579,581,583,585,588,590,592,594,597,599,601,604,606,608,611,613,615,618,620,622,625,627,629,632,634,636,639,641,643,646,648,651,653,655,658,660,663,665,668,670,672, -675,677,680,682,685,687,690,692,694,697,699,702,704,707,709,712,714,717,719,722,724,727,729,732,735,737,740,742,745,747,750,752,755,758,760,763,765,768,771,773,776,778,781,784,786,789,791,794,797,799, -802,805,807,810,813,815,818,821,823,826,829,831,834,837,840,842,845,848,850,853,856,859,861,864,867,870,872,875,878,881,883,886,889,892,895,897,900,903,906,909,911,914,917,920,923,926,928,931,934,937, -940,943,946,949,951,954,957,960,963,966,969,972,975,978,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085, -1088,1091,1095,1098,1101,1104,1107,1110,1113,1116,1119,1122,1126,1129,1132,1135,1138,1141,1144,1148,1151,1154,1157,1160,1163,1167,1170,1173,1176,1179,1183,1186,1189,1192,1195,1199,1202,1205,1208,1211,1215,1218,1221,1224,1228,1231,1234,1238,1241,1244, -1247,1251,1254,1257,1261,1264,1267,1270,1274,1277,1280,1284,1287,1290,1294,1297,1300,1304,1307,1310,1314,1317,1321,1324,1327,1331,1334,1338,1341,1344,1348,1351,1355,1358,1361,1365,1368,1372,1375,1379,1382,1385,1389,1392,1396,1399,1403,1406,1410,1413, -1417,1420,1424,1427,1431,1434,1438,1441,1445,1448,1452,1455,1459,1462,1466,1470,1473,1477,1480,1484,1487,1491,1494,1498,1502,1505,1509,1512,1516,1520,1523,1527,1530,1534,1538,1541,1545,1549,1552,1556,1560,1563,1567,1571,1574,1578,1582,1585,1589,1593, -1596,1600,1604,1607,1611,1615,1619,1622,1626,1630,1633,1637,1641,1645,1648,1652,1656,1660,1663,1667,1671,1675,1679,1682,1686,1690,1694,1698,1701,1705,1709,1713,1717,1720,1724,1728,1732,1736,1740,1743,1747,1751,1755,1759,1763,1767,1770,1774,1778,1782, -1786,1790,1794,1798,1802,1806,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845,1849,1853,1857,1861,1865,1869,1873,1877,1881,1885,1889,1893,1897,1901,1905,1909,1913,1917,1921,1925,1929,1933,1937,1941,1945,1949,1953,1957,1961,1965,1969,1974,1978,1982, -1986,1990,1994,1998,2002,2006,2010,2015,2019,2023,2027,2031,2035,2039,2043,2048,2052,2056,2060,2064,2068,2073,2077,2081,2085,2089,2094,2098,2102,2106,2110,2115,2119,2123,2127,2131,2136,2140,2144,2148,2153,2157,2161,2165,2170,2174,2178,2183,2187,2191, -2195,2200,2204,2208,2213,2217,2221,2226,2230,2234,2238,2243,2247,2251,2256,2260,2265,2269,2273,2278,2282,2286,2291,2295,2299,2304,2308,2313,2317,2321,2326,2330,2335,2339,2343,2348,2352,2357,2361,2366,2370,2375,2379,2383,2388,2392,2397,2401,2406,2410, -2415,2419,2424,2428,2433,2437,2442,2446,2451,2455,2460,2464,2469,2473,2478,2482,2487,2492,2496,2501,2505,2510,2514,2519,2523,2528,2533,2537,2542,2546,2551,2556,2560,2565,2569,2574,2579,2583,2588,2592,2597,2602,2606,2611,2616,2620,2625,2630,2634,2639, -2644,2648,2653,2658,2662,2667,2672,2676,2681,2686,2691,2695,2700,2705,2709,2714,2719,2724,2728,2733,2738,2743,2747,2752,2757,2762,2766,2771,2776,2781,2786,2790,2795,2800,2805,2810,2814,2819,2824,2829,2834,2838,2843,2848,2853,2858,2863,2867,2872,2877, -2882,2887,2892,2897,2901,2906,2911,2916,2921,2926,2931,2936,2941,2945,2950,2955,2960,2965,2970,2975,2980,2985,2990,2995,3000,3005,3010,3015,3020,3024,3029,3034,3039,3044,3049,3054,3059,3064,3069,3074,3079,3084,3089,3094,3099,3104,3109,3114,3119,3125, -3130,3135,3140,3145,3150,3155,3160,3165,3170,3175,3180,3185,3190,3195,3201,3206,3211,3216,3221,3226,3231,3236,3241,3247,3252,3257,3262,3267,3272,3277,3282,3288,3293,3298,3303,3308,3313,3319,3324,3329,3334,3339,3345,3350,3355,3360,3365,3371,3376,3381, -3386,3391,3397,3402,3407,3412,3418,3423,3428,3433,3439,3444,3449,3454,3460,3465,3470,3476,3481,3486,3491,3497,3502,3507,3513,3518,3523,3529,3534,3539,3545,3550,3555,3561,3566,3571,3577,3582,3587,3593,3598,3603,3609,3614,3619,3625,3630,3636,3641,3646, -3652,3657,3663,3668,3673,3679,3684,3690,3695,3701,3706,3711,3717,3722,3728,3733,3739,3744,3750,3755,3761,3766,3771,3777,3782,3788,3793,3799,3804,3810,3815,3821,3826,3832,3837,3843,3848,3854,3860,3865,3871,3876,3882,3887,3893,3898,3904,3909,3915,3921, -3926,3932,3937,3943,3948,3954,3960,3965,3971,3976,3982,3988,3993,3999,4004,4010,4016,4021,4027,4033,4038,4044,4050,4055,4061,4067,4072,4078,4083,4089,4095,4101,4106,4112,4118,4123,4129,4135,4140,4146,4152,4157,4163,4169,4175,4180,4186,4192,4198,4203, -4209,4215,4220,4226,4232,4238,4243,4249,4255,4261,4267,4272,4278,4284,4290,4295,4301,4307,4313,4319,4324,4330,4336,4342,4348,4354,4359,4365,4371,4377,4383,4389,4394,4400,4406,4412,4418,4424,4430,4435,4441,4447,4453,4459,4465,4471,4477,4482,4488,4494, -4500,4506,4512,4518,4524,4530,4536,4542,4548,4553,4559,4565,4571,4577,4583,4589,4595,4601,4607,4613,4619,4625,4631,4637,4643,4649,4655,4661,4667,4673,4679,4685,4691,4697,4703,4709,4715,4721,4727,4733,4739,4745,4751,4757,4763,4769,4775,4781,4787,4793, -4800,4806,4812,4818,4824,4830,4836,4842,4848,4854,4860,4866,4873,4879,4885,4891,4897,4903,4909,4915,4921,4928,4934,4940,4946,4952,4958,4964,4971,4977,4983,4989,4995,5001,5008,5014,5020,5026,5032,5039,5045,5051,5057,5063,5070,5076,5082,5088,5094,5101, -5107,5113,5119,5125,5132,5138,5144,5150,5157,5163,5169,5175,5182,5188,5194,5201,5207,5213,5219,5226,5232,5238,5244,5251,5257,5263,5270,5276,5282,5289,5295,5301,5308,5314,5320,5327,5333,5339,5346,5352,5358,5365,5371,5377,5384,5390,5396,5403,5409,5415, -5422,5428,5435,5441,5447,5454,5460,5467,5473,5479,5486,5492,5499,5505,5511,5518,5524,5531,5537,5544,5550,5556,5563,5569,5576,5582,5589,5595,5602,5608,5614,5621,5627,5634,5640,5647,5653,5660,5666,5673,5679,5686,5692,5699,5705,5712,5718,5725,5731,5738, -5744,5751,5757,5764,5770,5777,5784,5790,5797,5803,5810,5816,5823,5829,5836,5843,5849,5856,5862,5869,5875,5882,5889,5895,5902,5908,5915,5922,5928,5935,5941,5948,5955,5961,5968,5974,5981,5988,5994,6001,6008,6014,6021,6028,6034,6041,6048,6054,6061,6067, -6074,6081,6088,6094,6101,6108,6114,6121,6128,6134,6141,6148,6154,6161,6168,6174,6181,6188,6195,6201,6208,6215,6222,6228,6235,6242,6248,6255,6262,6269,6275,6282,6289,6296,6302,6309,6316,6323,6330,6336,6343,6350,6357,6363,6370,6377,6384,6391,6397,6404, -6411,6418,6425,6431,6438,6445,6452,6459,6466,6472,6479,6486,6493,6500,6507,6513,6520,6527,6534,6541,6548,6555,6561,6568,6575,6582,6589,6596,6603,6610,6616,6623,6630,6637,6644,6651,6658,6665,6672,6679,6685,6692,6699,6706,6713,6720,6727,6734,6741,6748, -6755,6762,6769,6776,6783,6790,6796,6803,6810,6817,6824,6831,6838,6845,6852,6859,6866,6873,6880,6887,6894,6901,6908,6915,6922,6929,6936,6943,6950,6957,6964,6971,6978,6985,6992,6999,7006,7013,7020,7027,7035,7042,7049,7056,7063,7070,7077,7084,7091,7098, -7105,7112,7119,7126,7133,7140,7148,7155,7162,7169,7176,7183,7190,7197,7204,7211,7219,7226,7233,7240,7247,7254,7261,7268,7276,7283,7290,7297,7304,7311,7318,7326,7333,7340,7347,7354,7361,7368,7376,7383,7390,7397,7404,7411,7419,7426,7433,7440,7447,7455, -7462,7469,7476,7483,7491,7498,7505,7512,7519,7527,7534,7541,7548,7556,7563,7570,7577,7584,7592,7599,7606,7613,7621,7628,7635,7642,7650,7657,7664,7671,7679,7686,7693,7701,7708,7715,7722,7730,7737,7744,7752,7759,7766,7773,7781,7788,7795,7803,7810,7817, -7825,7832,7839,7847,7854,7861,7869,7876,7883,7891,7898,7905,7913,7920,7927,7935,7942,7949,7957,7964,7971,7979,7986,7993,8001,8008,8016,8023,8030,8038,8045,8052,8060,8067,8075,8082,8089,8097,8104,8112,8119,8126,8134,8141,8149,8156,8164,8171,8178,8186, -8193,8201,8208,8216,8223,8230,8238,8245,8253,8260,8268,8275,8283,8290,8297,8305,8312,8320,8327,8335,8342,8350,8357,8365,8372,8380,8387,8395,8402,8410,8417,8425,8432,8440,8447,8455,8462,8470,8477,8485,8492,8500,8507,8515,8522,8530,8537,8545,8552,8560, -8568,8575,8583,8590,8598,8605,8613,8620,8628,8635,8643,8651,8658,8666,8673,8681,8688,8696,8704,8711,8719,8726,8734,8742,8749,8757,8764,8772,8779,8787,8795,8802,8810,8817,8825,8833,8840,8848,8856,8863,8871,8878,8886,8894,8901,8909,8917,8924,8932,8940, -8947,8955,8962,8970,8978,8985,8993,9001,9008,9016,9024,9031,9039,9047,9054,9062,9070,9077,9085,9093,9100,9108,9116,9124,9131,9139,9147,9154,9162,9170,9177,9185,9193,9201,9208,9216,9224,9231,9239,9247,9255,9262,9270,9278,9285,9293,9301,9309,9316,9324, -9332,9340,9347,9355,9363,9371,9378,9386,9394,9402,9409,9417,9425,9433,9440,9448,9456,9464,9472,9479,9487,9495,9503,9511,9518,9526,9534,9542,9549,9557,9565,9573,9581,9588,9596,9604,9612,9620,9628,9635,9643,9651,9659,9667,9674,9682,9690,9698,9706,9714, -9721,9729,9737,9745,9753,9761,9769,9776,9784,9792,9800,9808,9816,9824,9831,9839,9847,9855,9863,9871,9879,9886,9894,9902,9910,9918,9926,9934,9942,9950,9957,9965,9973,9981,9989,9997,10005,10013,10021,10029,10036,10044,10052,10060,10068,10076,10084,10092,10100,10108, -10116,10124,10131,10139,10147,10155,10163,10171,10179,10187,10195,10203,10211,10219,10227,10235,10243,10251,10259,10267,10274,10282,10290,10298,10306,10314,10322,10330,10338,10346,10354,10362,10370,10378,10386,10394,10402,10410,10418,10426,10434,10442,10450,10458,10466,10474,10482,10490,10498,10506, -10514,10522,10530,10538,10546,10554,10562,10570,10578,10586,10594,10602,10610,10618,10626,10634,10642,10650,10658,10667,10675,10683,10691,10699,10707,10715,10723,10731,10739,10747,10755,10763,10771,10779,10787,10795,10803,10811,10820,10828,10836,10844,10852,10860,10868,10876,10884,10892,10900,10908, -10916,10925,10933,10941,10949,10957,10965,10973,10981,10989,10997,11006,11014,11022,11030,11038,11046,11054,11062,11070,11079,11087,11095,11103,11111,11119,11127,11135,11144,11152,11160,11168,11176,11184,11192,11200,11209,11217,11225,11233,11241,11249,11257,11266,11274,11282,11290,11298,11306,11315, -11323,11331,11339,11347,11355,11364,11372,11380,11388,11396,11404,11413,11421,11429,11437,11445,11453,11462,11470,11478,11486,11494,11503,11511,11519,11527,11535,11544,11552,11560,11568,11576,11585,11593,11601,11609,11617,11626,11634,11642,11650,11658,11667,11675,11683,11691,11699,11708,11716,11724, -11732,11741,11749,11757,11765,11774,11782,11790,11798,11806,11815,11823,11831,11839,11848,11856,11864,11872,11881,11889,11897,11905,11914,11922,11930,11938,11947,11955,11963,11971,11980,11988,11996,12005,12013,12021,12029,12038,12046,12054,12062,12071,12079,12087,12096,12104,12112,12120,12129,12137, -12145,12154,12162,12170,12178,12187,12195,12203,12212,12220,12228,12236,12245,12253,12261,12270,12278,12286,12295,12303,12311,12320,12328,12336,12344,12353,12361,12369,12378,12386,12394,12403,12411,12419,12428,12436,12444,12453,12461,12469,12478,12486,12494,12503,12511,12519,12528,12536,12544,12553, -12561,12569,12578,12586,12594,12603,12611,12619,12628,12636,12644,12653,12661,12670,12678,12686,12695,12703,12711,12720,12728,12736,12745,12753,12762,12770,12778,12787,12795,12803,12812,12820,12828,12837,12845,12854,12862,12870,12879,12887,12896,12904,12912,12921,12929,12937,12946,12954,12963,12971, -12979,12988,12996,13005,13013,13021,13030,13038,13047,13055,13063,13072,13080,13089,13097,13105,13114,13122,13131,13139,13147,13156,13164,13173,13181,13189,13198,13206,13215,13223,13232,13240,13248,13257,13265,13274,13282,13291,13299,13307,13316,13324,13333,13341,13350,13358,13366,13375,13383,13392, -13400,13409,13417,13425,13434,13442,13451,13459,13468,13476,13485,13493,13501,13510,13518,13527,13535,13544,13552,13561,13569,13577,13586,13594,13603,13611,13620,13628,13637,13645,13654,13662,13670,13679,13687,13696,13704,13713,13721,13730,13738,13747,13755,13764,13772,13781,13789,13797,13806,13814, -13823,13831,13840,13848,13857,13865,13874,13882,13891,13899,13908,13916,13925,13933,13942,13950,13959,13967,13976,13984,13992,14001,14009,14018,14026,14035,14043,14052,14060,14069,14077,14086,14094,14103,14111,14120,14128,14137,14145,14154,14162,14171,14179,14188,14196,14205,14213,14222,14230,14239, -14247,14256,14264,14273,14281,14290,14298,14307,14315,14324,14332,14341,14350,14358,14367,14375,14384,14392,14401,14409,14418,14426,14435,14443,14452,14460,14469,14477,14486,14494,14503,14511,14520,14528,14537,14545,14554,14563,14571,14580,14588,14597,14605,14614,14622,14631,14639,14648,14656,14665, -14673,14682,14690,14699,14708,14716,14725,14733,14742,14750,14759,14767,14776,14784,14793,14801,14810,14819,14827,14836,14844,14853,14861,14870,14878,14887,14895,14904,14912,14921,14930,14938,14947,14955,14964,14972,14981,14989,14998,15006,15015,15024,15032,15041,15049,15058,15066,15075,15083,15092, -15101,15109,15118,15126,15135,15143,15152,15160,15169,15178,15186,15195,15203,15212,15220,15229,15237,15246,15255,15263,15272,15280,15289,15297,15306,15314,15323,15332,15340,15349,15357,15366,15374,15383,15392,15400,15409,15417,15426,15434,15443,15451,15460,15469,15477,15486,15494,15503,15511,15520, -15529,15537,15546,15554,15563,15571,15580,15589,15597,15606,15614,15623,15631,15640,15649,15657,15666,15674,15683,15691,15700,15709,15717,15726,15734,15743,15751,15760,15769,15777,15786,15794,15803,15811,15820,15829,15837,15846,15854,15863,15871,15880,15889,15897,15906,15914,15923,15931,15940,15949, -15957,15966,15974,15983,15992,16000,16009,16017,16026,16034,16043,16052,16060,16069,16077,16086,16094,16103,16112,16120,16129,16137,16146,16155,16163,16172,16180,16189,16197,16206,16215,16223,16232,16240,16249,16257,16266,16275,16283,16292,16300,16309,16318,16326,16335,16343,16352,16360,16369,16378, -16386,16395,16403,16412,16420,16429,16438,16446,16455,16463,16472,16481,16489,16498,16506,16515,16523,16532,16541,16549,16558,16566,16575,16583,16592,16601,16609,16618,16626,16635,16644,16652,16661,16669,16678,16686,16695,16704,16712,16721,16729,16738,16746,16755,16764,16772,16781,16789,16798,16806, -16815,16824,16832,16841,16849,16858,16867,16875,16884,16892,16901,16909,16918,16927,16935,16944,16952,16961,16969,16978,16987,16995,17004,17012,17021,17029,17038,17047,17055,17064,17072,17081,17089,17098,17107,17115,17124,17132,17141,17149,17158,17167,17175,17184,17192,17201,17209,17218,17227,17235, -17244,17252,17261,17269,17278,17287,17295,17304,17312,17321,17329,17338,17346,17355,17364,17372,17381,17389,17398,17406,17415,17424,17432,17441,17449,17458,17466,17475,17483,17492,17501,17509,17518,17526,17535,17543,17552,17561,17569,17578,17586,17595,17603,17612,17620,17629,17638,17646,17655,17663, -17672,17680,17689,17697,17706,17714,17723,17732,17740,17749,17757,17766,17774,17783,17791,17800,17809,17817,17826,17834,17843,17851,17860,17868,17877,17885,17894,17903,17911,17920,17928,17937,17945,17954,17962,17971,17979,17988,17996,18005,18014,18022,18031,18039,18048,18056,18065,18073,18082,18090, -18099,18107,18116,18124,18133,18142,18150,18159,18167,18176,18184,18193,18201,18210,18218,18227,18235,18244,18252,18261,18269,18278,18286,18295,18304,18312,18321,18329,18338,18346,18355,18363,18372,18380,18389,18397,18406,18414,18423,18431,18440,18448,18457,18465,18474,18482,18491,18499,18508,18516, -18525,18533,18542,18550,18559,18567,18576,18584,18593,18601,18610,18618,18627,18635,18644,18652,18661,18669,18678,18686,18695,18703,18712,18720,18729,18737,18746,18754,18763,18771,18780,18788,18797,18805,18814,18822,18831,18839,18848,18856,18865,18873,18882,18890,18898,18907,18915,18924,18932,18941, -18949,18958,18966,18975,18983,18992,19000,19009,19017,19026,19034,19043,19051,19059,19068,19076,19085,19093,19102,19110,19119,19127,19136,19144,19153,19161,19169,19178,19186,19195,19203,19212,19220,19229,19237,19245,19254,19262,19271,19279,19288,19296,19305,19313,19321,19330,19338,19347,19355,19364, -19372,19381,19389,19397,19406,19414,19423,19431,19440,19448,19456,19465,19473,19482,19490,19499,19507,19515,19524,19532,19541,19549,19557,19566,19574,19583,19591,19600,19608,19616,19625,19633,19642,19650,19658,19667,19675,19684,19692,19700,19709,19717,19726,19734,19742,19751,19759,19768,19776,19784, -19793,19801,19810,19818,19826,19835,19843,19852,19860,19868,19877,19885,19893,19902,19910,19919,19927,19935,19944,19952,19960,19969,19977,19986,19994,20002,20011,20019,20027,20036,20044,20052,20061,20069,20078,20086,20094,20103,20111,20119,20128,20136,20144,20153,20161,20169,20178,20186,20194,20203, -20211,20220,20228,20236,20245,20253,20261,20270,20278,20286,20295,20303,20311,20320,20328,20336,20345,20353,20361,20369,20378,20386,20394,20403,20411,20419,20428,20436,20444,20453,20461,20469,20478,20486,20494,20502,20511,20519,20527,20536,20544,20552,20561,20569,20577,20585,20594,20602,20610,20619, -20627,20635,20643,20652,20660,20668,20677,20685,20693,20701,20710,20718,20726,20735,20743,20751,20759,20768,20776,20784,20792,20801,20809,20817,20825,20834,20842,20850,20858,20867,20875,20883,20891,20900,20908,20916,20924,20933,20941,20949,20957,20966,20974,20982,20990,20999,21007,21015,21023,21032, -21040,21048,21056,21064,21073,21081,21089,21097,21106,21114,21122,21130,21138,21147,21155,21163,21171,21179,21188,21196,21204,21212,21220,21229,21237,21245,21253,21261,21270,21278,21286,21294,21302,21310,21319,21327,21335,21343,21351,21360,21368,21376,21384,21392,21400,21409,21417,21425,21433,21441, -21449,21458,21466,21474,21482,21490,21498,21506,21515,21523,21531,21539,21547,21555,21563,21572,21580,21588,21596,21604,21612,21620,21629,21637,21645,21653,21661,21669,21677,21685,21694,21702,21710,21718,21726,21734,21742,21750,21758,21767,21775,21783,21791,21799,21807,21815,21823,21831,21839,21847, -21856,21864,21872,21880,21888,21896,21904,21912,21920,21928,21936,21944,21952,21961,21969,21977,21985,21993,22001,22009,22017,22025,22033,22041,22049,22057,22065,22073,22081,22089,22097,22106,22114,22122,22130,22138,22146,22154,22162,22170,22178,22186,22194,22202,22210,22218,22226,22234,22242,22250, -22258,22266,22274,22282,22290,22298,22306,22314,22322,22330,22338,22346,22354,22362,22370,22378,22386,22394,22402,22410,22418,22426,22434,22442,22450,22458,22466,22474,22482,22490,22498,22505,22513,22521,22529,22537,22545,22553,22561,22569,22577,22585,22593,22601,22609,22617,22625,22633,22641,22648, -22656,22664,22672,22680,22688,22696,22704,22712,22720,22728,22736,22743,22751,22759,22767,22775,22783,22791,22799,22807,22815,22822,22830,22838,22846,22854,22862,22870,22878,22885,22893,22901,22909,22917,22925,22933,22941,22948,22956,22964,22972,22980,22988,22996,23003,23011,23019,23027,23035,23043, -23050,23058,23066,23074,23082,23090,23097,23105,23113,23121,23129,23137,23144,23152,23160,23168,23176,23183,23191,23199,23207,23215,23222,23230,23238,23246,23254,23261,23269,23277,23285,23293,23300,23308,23316,23324,23331,23339,23347,23355,23362,23370,23378,23386,23393,23401,23409,23417,23424,23432, -23440,23448,23455,23463,23471,23479,23486,23494,23502,23510,23517,23525,23533,23540,23548,23556,23564,23571,23579,23587,23594,23602,23610,23618,23625,23633,23641,23648,23656,23664,23671,23679,23687,23694,23702,23710,23717,23725,23733,23740,23748,23756,23763,23771,23779,23786,23794,23802,23809,23817, -23825,23832,23840,23848,23855,23863,23870,23878,23886,23893,23901,23909,23916,23924,23931,23939,23947,23954,23962,23970,23977,23985,23992,24000,24008,24015,24023,24030,24038,24045,24053,24061,24068,24076,24083,24091,24098,24106,24114,24121,24129,24136,24144,24151,24159,24167,24174,24182,24189,24197, -24204,24212,24219,24227,24234,24242,24249,24257,24264,24272,24280,24287,24295,24302,24310,24317,24325,24332,24340,24347,24355,24362,24370,24377,24385,24392,24400,24407,24414,24422,24429,24437,24444,24452,24459,24467,24474,24482,24489,24497,24504,24512,24519,24526,24534,24541,24549,24556,24564,24571, -24578,24586,24593,24601,24608,24616,24623,24630,24638,24645,24653,24660,24667,24675,24682,24690,24697,24704,24712,24719,24727,24734,24741,24749,24756,24763,24771,24778,24786,24793,24800,24808,24815,24822,24830,24837,24844,24852,24859,24866,24874,24881,24888,24896,24903,24910,24918,24925,24932,24940, -24947,24954,24962,24969,24976,24984,24991,24998,25006,25013,25020,25027,25035,25042,25049,25057,25064,25071,25078,25086,25093,25100,25107,25115,25122,25129,25136,25144,25151,25158,25165,25173,25180,25187,25194,25202,25209,25216,25223,25231,25238,25245,25252,25259,25267,25274,25281,25288,25295,25303, -25310,25317,25324,25331,25339,25346,25353,25360,25367,25374,25382,25389,25396,25403,25410,25417,25425,25432,25439,25446,25453,25460,25468,25475,25482,25489,25496,25503,25510,25517,25525,25532,25539,25546,25553,25560,25567,25574,25581,25589,25596,25603,25610,25617,25624,25631,25638,25645,25652,25659, -25666,25674,25681,25688,25695,25702,25709,25716,25723,25730,25737,25744,25751,25758,25765,25772,25779,25786,25793,25800,25807,25814,25821,25828,25835,25842,25849,25856,25863,25870,25877,25884,25891,25898,25905,25912,25919,25926,25933,25940,25947,25954,25961,25968,25975,25982,25989,25996,26003,26010, -26017,26024,26031,26038,26044,26051,26058,26065,26072,26079,26086,26093,26100,26107,26114,26121,26127,26134,26141,26148,26155,26162,26169,26176,26182,26189,26196,26203,26210,26217,26224,26231,26237,26244,26251,26258,26265,26272,26278,26285,26292,26299,26306,26313,26319,26326,26333,26340,26347,26354, -26360,26367,26374,26381,26388,26394,26401,26408,26415,26421,26428,26435,26442,26449,26455,26462,26469,26476,26482,26489,26496,26503,26509,26516,26523,26530,26536,26543,26550,26557,26563,26570,26577,26583,26590,26597,26604,26610,26617,26624,26630,26637,26644,26650,26657,26664,26670,26677,26684,26691, -26697,26704,26711,26717,26724,26730,26737,26744,26750,26757,26764,26770,26777,26784,26790,26797,26803,26810,26817,26823,26830,26837,26843,26850,26856,26863,26870,26876,26883,26889,26896,26902,26909,26916,26922,26929,26935,26942,26948,26955,26962,26968,26975,26981,26988,26994,27001,27007,27014,27020, -27027,27033,27040,27046,27053,27059,27066,27073,27079,27086,27092,27098,27105,27111,27118,27124,27131,27137,27144,27150,27157,27163,27170,27176,27183,27189,27196,27202,27208,27215,27221,27228,27234,27241,27247,27253,27260,27266,27273,27279,27285,27292,27298,27305,27311,27317,27324,27330,27337,27343, -27349,27356,27362,27368,27375,27381,27388,27394,27400,27407,27413,27419,27426,27432,27438,27445,27451,27457,27464,27470,27476,27483,27489,27495,27501,27508,27514,27520,27527,27533,27539,27546,27552,27558,27564,27571,27577,27583,27589,27596,27602,27608,27614,27621,27627,27633,27639,27646,27652,27658, -27664,27671,27677,27683,27689,27695,27702,27708,27714,27720,27726,27733,27739,27745,27751,27757,27763,27770,27776,27782,27788,27794,27800,27807,27813,27819,27825,27831,27837,27843,27850,27856,27862,27868,27874,27880,27886,27892,27898,27905,27911,27917,27923,27929,27935,27941,27947,27953,27959,27965, -27972,27978,27984,27990,27996,28002,28008,28014,28020,28026,28032,28038,28044,28050,28056,28062,28068,28074,28080,28086,28092,28098,28104,28110,28116,28122,28128,28134,28140,28146,28152,28158,28164,28170,28176,28182,28188,28194,28200,28206,28212,28218,28223,28229,28235,28241,28247,28253,28259,28265, -28271,28277,28283,28288,28294,28300,28306,28312,28318,28324,28330,28336,28341,28347,28353,28359,28365,28371,28377,28382,28388,28394,28400,28406,28412,28417,28423,28429,28435,28441,28446,28452,28458,28464,28470,28475,28481,28487,28493,28499,28504,28510,28516,28522,28527,28533,28539,28545,28550,28556, -28562,28568,28573,28579,28585,28591,28596,28602,28608,28613,28619,28625,28631,28636,28642,28648,28653,28659,28665,28670,28676,28682,28687,28693,28699,28704,28710,28716,28721,28727,28733,28738,28744,28749,28755,28761,28766,28772,28778,28783,28789,28794,28800,28806,28811,28817,28822,28828,28834,28839, -28845,28850,28856,28861,28867,28872,28878,28884,28889,28895,28900,28906,28911,28917,28922,28928,28933,28939,28944,28950,28955,28961,28966,28972,28977,28983,28988,28994,28999,29005,29010,29016,29021,29027,29032,29038,29043,29048,29054,29059,29065,29070,29076,29081,29086,29092,29097,29103,29108,29113, -29119,29124,29130,29135,29140,29146,29151,29157,29162,29167,29173,29178,29183,29189,29194,29199,29205,29210,29215,29221,29226,29231,29237,29242,29247,29253,29258,29263,29269,29274,29279,29285,29290,29295,29300,29306,29311,29316,29321,29327,29332,29337,29342,29348,29353,29358,29363,29369,29374,29379, -29384,29390,29395,29400,29405,29410,29416,29421,29426,29431,29436,29442,29447,29452,29457,29462,29467,29473,29478,29483,29488,29493,29498,29504,29509,29514,29519,29524,29529,29534,29539,29545,29550,29555,29560,29565,29570,29575,29580,29585,29590,29595,29601,29606,29611,29616,29621,29626,29631,29636, -29641,29646,29651,29656,29661,29666,29671,29676,29681,29686,29691,29696,29701,29706,29711,29716,29721,29726,29731,29736,29741,29746,29751,29756,29761,29766,29771,29776,29781,29786,29791,29795,29800,29805,29810,29815,29820,29825,29830,29835,29840,29845,29849,29854,29859,29864,29869,29874,29879,29884, -29888,29893,29898,29903,29908,29913,29918,29922,29927,29932,29937,29942,29946,29951,29956,29961,29966,29970,29975,29980,29985,29990,29994,29999,30004,30009,30013,30018,30023,30028,30033,30037,30042,30047,30051,30056,30061,30066,30070,30075,30080,30084,30089,30094,30099,30103,30108,30113,30117,30122, -30127,30131,30136,30141,30145,30150,30155,30159,30164,30169,30173,30178,30182,30187,30192,30196,30201,30206,30210,30215,30219,30224,30229,30233,30238,30242,30247,30251,30256,30261,30265,30270,30274,30279,30283,30288,30292,30297,30301,30306,30311,30315,30320,30324,30329,30333,30338,30342,30347,30351, -30356,30360,30364,30369,30373,30378,30382,30387,30391,30396,30400,30405,30409,30413,30418,30422,30427,30431,30436,30440,30444,30449,30453,30458,30462,30466,30471,30475,30479,30484,30488,30493,30497,30501,30506,30510,30514,30519,30523,30527,30532,30536,30540,30545,30549,30553,30558,30562,30566,30570, -30575,30579,30583,30588,30592,30596,30600,30605,30609,30613,30617,30622,30626,30630,30634,30639,30643,30647,30651,30656,30660,30664,30668,30672,30677,30681,30685,30689,30693,30697,30702,30706,30710,30714,30718,30722,30727,30731,30735,30739,30743,30747,30751,30756,30760,30764,30768,30772,30776,30780, -30784,30788,30792,30797,30801,30805,30809,30813,30817,30821,30825,30829,30833,30837,30841,30845,30849,30853,30857,30861,30865,30869,30873,30877,30881,30885,30889,30893,30897,30901,30905,30909,30913,30917,30921,30925,30929,30933,30937,30941,30945,30949,30953,30957,30960,30964,30968,30972,30976,30980, -30984,30988,30992,30996,30999,31003,31007,31011,31015,31019,31023,31026,31030,31034,31038,31042,31046,31050,31053,31057,31061,31065,31069,31072,31076,31080,31084,31088,31091,31095,31099,31103,31106,31110,31114,31118,31121,31125,31129,31133,31136,31140,31144,31148,31151,31155,31159,31162,31166,31170, -31174,31177,31181,31185,31188,31192,31196,31199,31203,31207,31210,31214,31218,31221,31225,31228,31232,31236,31239,31243,31247,31250,31254,31257,31261,31265,31268,31272,31275,31279,31282,31286,31290,31293,31297,31300,31304,31307,31311,31314,31318,31321,31325,31329,31332,31336,31339,31343,31346,31350, -31353,31357,31360,31363,31367,31370,31374,31377,31381,31384,31388,31391,31395,31398,31401,31405,31408,31412,31415,31419,31422,31425,31429,31432,31436,31439,31442,31446,31449,31452,31456,31459,31463,31466,31469,31473,31476,31479,31483,31486,31489,31493,31496,31499,31503,31506,31509,31512,31516,31519, -31522,31526,31529,31532,31535,31539,31542,31545,31548,31552,31555,31558,31561,31565,31568,31571,31574,31577,31581,31584,31587,31590,31593,31597,31600,31603,31606,31609,31613,31616,31619,31622,31625,31628,31631,31635,31638,31641,31644,31647,31650,31653,31656,31660,31663,31666,31669,31672,31675,31678, -31681,31684,31687,31690,31693,31696,31700,31703,31706,31709,31712,31715,31718,31721,31724,31727,31730,31733,31736,31739,31742,31745,31748,31751,31754,31757,31760,31763,31766,31768,31771,31774,31777,31780,31783,31786,31789,31792,31795,31798,31801,31804,31806,31809,31812,31815,31818,31821,31824,31827, -31830,31832,31835,31838,31841,31844,31847,31849,31852,31855,31858,31861,31864,31866,31869,31872,31875,31878,31880,31883,31886,31889,31891,31894,31897,31900,31902,31905,31908,31911,31913,31916,31919,31922,31924,31927,31930,31933,31935,31938,31941,31943,31946,31949,31951,31954,31957,31959,31962,31965, -31967,31970,31973,31975,31978,31980,31983,31986,31988,31991,31994,31996,31999,32001,32004,32007,32009,32012,32014,32017,32019,32022,32025,32027,32030,32032,32035,32037,32040,32042,32045,32047,32050,32052,32055,32057,32060,32062,32065,32067,32070,32072,32075,32077,32080,32082,32085,32087,32090,32092, -32094,32097,32099,32102,32104,32107,32109,32111,32114,32116,32119,32121,32123,32126,32128,32130,32133,32135,32138,32140,32142,32145,32147,32149,32152,32154,32156,32159,32161,32163,32166,32168,32170,32172,32175,32177,32179,32182,32184,32186,32188,32191,32193,32195,32197,32200,32202,32204,32206,32209, -32211,32213,32215,32217,32220,32222,32224,32226,32228,32231,32233,32235,32237,32239,32241,32244,32246,32248,32250,32252,32254,32256,32259,32261,32263,32265,32267,32269,32271,32273,32275,32277,32280,32282,32284,32286,32288,32290,32292,32294,32296,32298,32300,32302,32304,32306,32308,32310,32312,32314, -32316,32318,32320,32322,32324,32326,32328,32330,32332,32334,32336,32338,32340,32342,32344,32346,32348,32350,32352,32353,32355,32357,32359,32361,32363,32365,32367,32369,32371,32372,32374,32376,32378,32380,32382,32384,32385,32387,32389,32391,32393,32395,32396,32398,32400,32402,32404,32405,32407,32409, -32411,32413,32414,32416,32418,32420,32421,32423,32425,32427,32428,32430,32432,32434,32435,32437,32439,32440,32442,32444,32446,32447,32449,32451,32452,32454,32456,32457,32459,32461,32462,32464,32466,32467,32469,32470,32472,32474,32475,32477,32479,32480,32482,32483,32485,32487,32488,32490,32491,32493, -32494,32496,32497,32499,32501,32502,32504,32505,32507,32508,32510,32511,32513,32514,32516,32517,32519,32520,32522,32523,32525,32526,32528,32529,32531,32532,32533,32535,32536,32538,32539,32541,32542,32544,32545,32546,32548,32549,32551,32552,32553,32555,32556,32557,32559,32560,32562,32563,32564,32566, -32567,32568,32570,32571,32572,32574,32575,32576,32578,32579,32580,32581,32583,32584,32585,32587,32588,32589,32590,32592,32593,32594,32595,32597,32598,32599,32600,32601,32603,32604,32605,32606,32608,32609,32610,32611,32612,32613,32615,32616,32617,32618,32619,32620,32622,32623,32624,32625,32626,32627, -32628,32629,32631,32632,32633,32634,32635,32636,32637,32638,32639,32640,32641,32643,32644,32645,32646,32647,32648,32649,32650,32651,32652,32653,32654,32655,32656,32657,32658,32659,32660,32661,32662,32663,32664,32665,32666,32667,32668,32669,32670,32670,32671,32672,32673,32674,32675,32676,32677,32678, -32679,32680,32680,32681,32682,32683,32684,32685,32686,32687,32687,32688,32689,32690,32691,32692,32692,32693,32694,32695,32696,32696,32697,32698,32699,32700,32700,32701,32702,32703,32704,32704,32705,32706,32707,32707,32708,32709,32709,32710,32711,32712,32712,32713,32714,32714,32715,32716,32716,32717, -32718,32718,32719,32720,32720,32721,32722,32722,32723,32724,32724,32725,32726,32726,32727,32727,32728,32729,32729,32730,32730,32731,32731,32732,32733,32733,32734,32734,32735,32735,32736,32736,32737,32738,32738,32739,32739,32740,32740,32741,32741,32742,32742,32743,32743,32743,32744,32744,32745,32745, -32746,32746,32747,32747,32748,32748,32748,32749,32749,32750,32750,32750,32751,32751,32752,32752,32752,32753,32753,32753,32754,32754,32755,32755,32755,32756,32756,32756,32757,32757,32757,32757,32758,32758,32758,32759,32759,32759,32760,32760,32760,32760,32761,32761,32761,32761,32762,32762,32762,32762, -32762,32763,32763,32763,32763,32764,32764,32764,32764,32764,32764,32765,32765,32765,32765,32765,32765,32766,32766,32766,32766,32766,32766,32766,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768, -32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32766,32766,32766,32766,32766,32766,32766,32765,32765,32765,32765,32765,32765,32764,32764,32764,32764,32764,32764,32763,32763,32763,32763,32762, -32762,32762,32762,32762,32761,32761,32761,32761,32760,32760,32760,32760,32759,32759,32759,32758,32758,32758,32757,32757,32757,32757,32756,32756,32756,32755,32755,32755,32754,32754,32753,32753,32753,32752,32752,32752,32751,32751,32750,32750,32750,32749,32749,32748,32748,32748,32747,32747,32746,32746, -32745,32745,32744,32744,32743,32743,32743,32742,32742,32741,32741,32740,32740,32739,32739,32738,32738,32737,32736,32736,32735,32735,32734,32734,32733,32733,32732,32731,32731,32730,32730,32729,32729,32728,32727,32727,32726,32726,32725,32724,32724,32723,32722,32722,32721,32720,32720,32719,32718,32718, -32717,32716,32716,32715,32714,32714,32713,32712,32712,32711,32710,32709,32709,32708,32707,32707,32706,32705,32704,32704,32703,32702,32701,32700,32700,32699,32698,32697,32696,32696,32695,32694,32693,32692,32692,32691,32690,32689,32688,32687,32687,32686,32685,32684,32683,32682,32681,32680,32680,32679, -32678,32677,32676,32675,32674,32673,32672,32671,32670,32670,32669,32668,32667,32666,32665,32664,32663,32662,32661,32660,32659,32658,32657,32656,32655,32654,32653,32652,32651,32650,32649,32648,32647,32646,32645,32644,32643,32641,32640,32639,32638,32637,32636,32635,32634,32633,32632,32631,32629,32628, -32627,32626,32625,32624,32623,32622,32620,32619,32618,32617,32616,32615,32613,32612,32611,32610,32609,32608,32606,32605,32604,32603,32601,32600,32599,32598,32597,32595,32594,32593,32592,32590,32589,32588,32587,32585,32584,32583,32581,32580,32579,32578,32576,32575,32574,32572,32571,32570,32568,32567, -32566,32564,32563,32562,32560,32559,32557,32556,32555,32553,32552,32551,32549,32548,32546,32545,32544,32542,32541,32539,32538,32536,32535,32533,32532,32531,32529,32528,32526,32525,32523,32522,32520,32519,32517,32516,32514,32513,32511,32510,32508,32507,32505,32504,32502,32501,32499,32497,32496,32494, -32493,32491,32490,32488,32487,32485,32483,32482,32480,32479,32477,32475,32474,32472,32470,32469,32467,32466,32464,32462,32461,32459,32457,32456,32454,32452,32451,32449,32447,32446,32444,32442,32440,32439,32437,32435,32434,32432,32430,32428,32427,32425,32423,32421,32420,32418,32416,32414,32413,32411, -32409,32407,32405,32404,32402,32400,32398,32396,32395,32393,32391,32389,32387,32385,32384,32382,32380,32378,32376,32374,32372,32371,32369,32367,32365,32363,32361,32359,32357,32355,32353,32352,32350,32348,32346,32344,32342,32340,32338,32336,32334,32332,32330,32328,32326,32324,32322,32320,32318,32316, -32314,32312,32310,32308,32306,32304,32302,32300,32298,32296,32294,32292,32290,32288,32286,32284,32282,32280,32277,32275,32273,32271,32269,32267,32265,32263,32261,32259,32256,32254,32252,32250,32248,32246,32244,32241,32239,32237,32235,32233,32231,32228,32226,32224,32222,32220,32217,32215,32213,32211, -32209,32206,32204,32202,32200,32197,32195,32193,32191,32188,32186,32184,32182,32179,32177,32175,32172,32170,32168,32166,32163,32161,32159,32156,32154,32152,32149,32147,32145,32142,32140,32138,32135,32133,32130,32128,32126,32123,32121,32119,32116,32114,32111,32109,32107,32104,32102,32099,32097,32094, -32092,32090,32087,32085,32082,32080,32077,32075,32072,32070,32067,32065,32062,32060,32057,32055,32052,32050,32047,32045,32042,32040,32037,32035,32032,32030,32027,32025,32022,32019,32017,32014,32012,32009,32007,32004,32001,31999,31996,31994,31991,31988,31986,31983,31980,31978,31975,31973,31970,31967, -31965,31962,31959,31957,31954,31951,31949,31946,31943,31941,31938,31935,31933,31930,31927,31924,31922,31919,31916,31913,31911,31908,31905,31902,31900,31897,31894,31891,31889,31886,31883,31880,31878,31875,31872,31869,31866,31864,31861,31858,31855,31852,31849,31847,31844,31841,31838,31835,31832,31830, -31827,31824,31821,31818,31815,31812,31809,31806,31804,31801,31798,31795,31792,31789,31786,31783,31780,31777,31774,31771,31768,31766,31763,31760,31757,31754,31751,31748,31745,31742,31739,31736,31733,31730,31727,31724,31721,31718,31715,31712,31709,31706,31703,31700,31696,31693,31690,31687,31684,31681, -31678,31675,31672,31669,31666,31663,31660,31656,31653,31650,31647,31644,31641,31638,31635,31631,31628,31625,31622,31619,31616,31613,31609,31606,31603,31600,31597,31593,31590,31587,31584,31581,31577,31574,31571,31568,31565,31561,31558,31555,31552,31548,31545,31542,31539,31535,31532,31529,31526,31522, -31519,31516,31512,31509,31506,31503,31499,31496,31493,31489,31486,31483,31479,31476,31473,31469,31466,31463,31459,31456,31452,31449,31446,31442,31439,31436,31432,31429,31425,31422,31419,31415,31412,31408,31405,31401,31398,31395,31391,31388,31384,31381,31377,31374,31370,31367,31363,31360,31357,31353, -31350,31346,31343,31339,31336,31332,31329,31325,31321,31318,31314,31311,31307,31304,31300,31297,31293,31290,31286,31282,31279,31275,31272,31268,31265,31261,31257,31254,31250,31247,31243,31239,31236,31232,31228,31225,31221,31218,31214,31210,31207,31203,31199,31196,31192,31188,31185,31181,31177,31174, -31170,31166,31162,31159,31155,31151,31148,31144,31140,31136,31133,31129,31125,31121,31118,31114,31110,31106,31103,31099,31095,31091,31088,31084,31080,31076,31072,31069,31065,31061,31057,31053,31050,31046,31042,31038,31034,31030,31026,31023,31019,31015,31011,31007,31003,30999,30996,30992,30988,30984, -30980,30976,30972,30968,30964,30960,30957,30953,30949,30945,30941,30937,30933,30929,30925,30921,30917,30913,30909,30905,30901,30897,30893,30889,30885,30881,30877,30873,30869,30865,30861,30857,30853,30849,30845,30841,30837,30833,30829,30825,30821,30817,30813,30809,30805,30801,30797,30792,30788,30784, -30780,30776,30772,30768,30764,30760,30756,30751,30747,30743,30739,30735,30731,30727,30722,30718,30714,30710,30706,30702,30697,30693,30689,30685,30681,30677,30672,30668,30664,30660,30656,30651,30647,30643,30639,30634,30630,30626,30622,30617,30613,30609,30605,30600,30596,30592,30588,30583,30579,30575, -30570,30566,30562,30558,30553,30549,30545,30540,30536,30532,30527,30523,30519,30514,30510,30506,30501,30497,30493,30488,30484,30479,30475,30471,30466,30462,30458,30453,30449,30444,30440,30436,30431,30427,30422,30418,30413,30409,30405,30400,30396,30391,30387,30382,30378,30373,30369,30364,30360,30356, -30351,30347,30342,30338,30333,30329,30324,30320,30315,30311,30306,30301,30297,30292,30288,30283,30279,30274,30270,30265,30261,30256,30251,30247,30242,30238,30233,30229,30224,30219,30215,30210,30206,30201,30196,30192,30187,30182,30178,30173,30169,30164,30159,30155,30150,30145,30141,30136,30131,30127, -30122,30117,30113,30108,30103,30099,30094,30089,30084,30080,30075,30070,30066,30061,30056,30051,30047,30042,30037,30033,30028,30023,30018,30013,30009,30004,29999,29994,29990,29985,29980,29975,29970,29966,29961,29956,29951,29946,29942,29937,29932,29927,29922,29918,29913,29908,29903,29898,29893,29888, -29884,29879,29874,29869,29864,29859,29854,29849,29845,29840,29835,29830,29825,29820,29815,29810,29805,29800,29795,29791,29786,29781,29776,29771,29766,29761,29756,29751,29746,29741,29736,29731,29726,29721,29716,29711,29706,29701,29696,29691,29686,29681,29676,29671,29666,29661,29656,29651,29646,29641, -29636,29631,29626,29621,29616,29611,29606,29601,29595,29590,29585,29580,29575,29570,29565,29560,29555,29550,29545,29539,29534,29529,29524,29519,29514,29509,29504,29498,29493,29488,29483,29478,29473,29467,29462,29457,29452,29447,29442,29436,29431,29426,29421,29416,29410,29405,29400,29395,29390,29384, -29379,29374,29369,29363,29358,29353,29348,29342,29337,29332,29327,29321,29316,29311,29306,29300,29295,29290,29285,29279,29274,29269,29263,29258,29253,29247,29242,29237,29231,29226,29221,29215,29210,29205,29199,29194,29189,29183,29178,29173,29167,29162,29157,29151,29146,29140,29135,29130,29124,29119, -29113,29108,29103,29097,29092,29086,29081,29076,29070,29065,29059,29054,29048,29043,29038,29032,29027,29021,29016,29010,29005,28999,28994,28988,28983,28977,28972,28966,28961,28955,28950,28944,28939,28933,28928,28922,28917,28911,28906,28900,28895,28889,28884,28878,28872,28867,28861,28856,28850,28845, -28839,28834,28828,28822,28817,28811,28806,28800,28794,28789,28783,28778,28772,28766,28761,28755,28749,28744,28738,28733,28727,28721,28716,28710,28704,28699,28693,28687,28682,28676,28670,28665,28659,28653,28648,28642,28636,28631,28625,28619,28613,28608,28602,28596,28591,28585,28579,28573,28568,28562, -28556,28550,28545,28539,28533,28527,28522,28516,28510,28504,28499,28493,28487,28481,28475,28470,28464,28458,28452,28446,28441,28435,28429,28423,28417,28412,28406,28400,28394,28388,28382,28377,28371,28365,28359,28353,28347,28341,28336,28330,28324,28318,28312,28306,28300,28294,28288,28283,28277,28271, -28265,28259,28253,28247,28241,28235,28229,28223,28218,28212,28206,28200,28194,28188,28182,28176,28170,28164,28158,28152,28146,28140,28134,28128,28122,28116,28110,28104,28098,28092,28086,28080,28074,28068,28062,28056,28050,28044,28038,28032,28026,28020,28014,28008,28002,27996,27990,27984,27978,27972, -27965,27959,27953,27947,27941,27935,27929,27923,27917,27911,27905,27898,27892,27886,27880,27874,27868,27862,27856,27850,27843,27837,27831,27825,27819,27813,27807,27800,27794,27788,27782,27776,27770,27763,27757,27751,27745,27739,27733,27726,27720,27714,27708,27702,27695,27689,27683,27677,27671,27664, -27658,27652,27646,27639,27633,27627,27621,27614,27608,27602,27596,27589,27583,27577,27571,27564,27558,27552,27546,27539,27533,27527,27520,27514,27508,27501,27495,27489,27483,27476,27470,27464,27457,27451,27445,27438,27432,27426,27419,27413,27407,27400,27394,27388,27381,27375,27368,27362,27356,27349, -27343,27337,27330,27324,27317,27311,27305,27298,27292,27285,27279,27273,27266,27260,27253,27247,27241,27234,27228,27221,27215,27208,27202,27196,27189,27183,27176,27170,27163,27157,27150,27144,27137,27131,27124,27118,27111,27105,27098,27092,27086,27079,27073,27066,27059,27053,27046,27040,27033,27027, -27020,27014,27007,27001,26994,26988,26981,26975,26968,26962,26955,26948,26942,26935,26929,26922,26916,26909,26902,26896,26889,26883,26876,26870,26863,26856,26850,26843,26837,26830,26823,26817,26810,26803,26797,26790,26784,26777,26770,26764,26757,26750,26744,26737,26730,26724,26717,26711,26704,26697, -26691,26684,26677,26670,26664,26657,26650,26644,26637,26630,26624,26617,26610,26604,26597,26590,26583,26577,26570,26563,26557,26550,26543,26536,26530,26523,26516,26509,26503,26496,26489,26482,26476,26469,26462,26455,26449,26442,26435,26428,26421,26415,26408,26401,26394,26388,26381,26374,26367,26360, -26354,26347,26340,26333,26326,26319,26313,26306,26299,26292,26285,26278,26272,26265,26258,26251,26244,26237,26231,26224,26217,26210,26203,26196,26189,26182,26176,26169,26162,26155,26148,26141,26134,26127,26121,26114,26107,26100,26093,26086,26079,26072,26065,26058,26051,26044,26038,26031,26024,26017, -26010,26003,25996,25989,25982,25975,25968,25961,25954,25947,25940,25933,25926,25919,25912,25905,25898,25891,25884,25877,25870,25863,25856,25849,25842,25835,25828,25821,25814,25807,25800,25793,25786,25779,25772,25765,25758,25751,25744,25737,25730,25723,25716,25709,25702,25695,25688,25681,25674,25666, -25659,25652,25645,25638,25631,25624,25617,25610,25603,25596,25589,25581,25574,25567,25560,25553,25546,25539,25532,25525,25517,25510,25503,25496,25489,25482,25475,25468,25460,25453,25446,25439,25432,25425,25417,25410,25403,25396,25389,25382,25374,25367,25360,25353,25346,25339,25331,25324,25317,25310, -25303,25295,25288,25281,25274,25267,25259,25252,25245,25238,25231,25223,25216,25209,25202,25194,25187,25180,25173,25165,25158,25151,25144,25136,25129,25122,25115,25107,25100,25093,25086,25078,25071,25064,25057,25049,25042,25035,25027,25020,25013,25006,24998,24991,24984,24976,24969,24962,24954,24947, -24940,24932,24925,24918,24910,24903,24896,24888,24881,24874,24866,24859,24852,24844,24837,24830,24822,24815,24808,24800,24793,24786,24778,24771,24763,24756,24749,24741,24734,24727,24719,24712,24704,24697,24690,24682,24675,24667,24660,24653,24645,24638,24630,24623,24616,24608,24601,24593,24586,24578, -24571,24564,24556,24549,24541,24534,24526,24519,24512,24504,24497,24489,24482,24474,24467,24459,24452,24444,24437,24429,24422,24414,24407,24400,24392,24385,24377,24370,24362,24355,24347,24340,24332,24325,24317,24310,24302,24295,24287,24280,24272,24264,24257,24249,24242,24234,24227,24219,24212,24204, -24197,24189,24182,24174,24167,24159,24151,24144,24136,24129,24121,24114,24106,24098,24091,24083,24076,24068,24061,24053,24045,24038,24030,24023,24015,24008,24000,23992,23985,23977,23970,23962,23954,23947,23939,23931,23924,23916,23909,23901,23893,23886,23878,23870,23863,23855,23848,23840,23832,23825, -23817,23809,23802,23794,23786,23779,23771,23763,23756,23748,23740,23733,23725,23717,23710,23702,23694,23687,23679,23671,23664,23656,23648,23641,23633,23625,23618,23610,23602,23594,23587,23579,23571,23564,23556,23548,23540,23533,23525,23517,23510,23502,23494,23486,23479,23471,23463,23455,23448,23440, -23432,23424,23417,23409,23401,23393,23386,23378,23370,23362,23355,23347,23339,23331,23324,23316,23308,23300,23293,23285,23277,23269,23261,23254,23246,23238,23230,23222,23215,23207,23199,23191,23183,23176,23168,23160,23152,23144,23137,23129,23121,23113,23105,23097,23090,23082,23074,23066,23058,23050, -23043,23035,23027,23019,23011,23003,22996,22988,22980,22972,22964,22956,22948,22941,22933,22925,22917,22909,22901,22893,22885,22878,22870,22862,22854,22846,22838,22830,22822,22815,22807,22799,22791,22783,22775,22767,22759,22751,22743,22736,22728,22720,22712,22704,22696,22688,22680,22672,22664,22656, -22648,22641,22633,22625,22617,22609,22601,22593,22585,22577,22569,22561,22553,22545,22537,22529,22521,22513,22505,22498,22490,22482,22474,22466,22458,22450,22442,22434,22426,22418,22410,22402,22394,22386,22378,22370,22362,22354,22346,22338,22330,22322,22314,22306,22298,22290,22282,22274,22266,22258, -22250,22242,22234,22226,22218,22210,22202,22194,22186,22178,22170,22162,22154,22146,22138,22130,22122,22114,22106,22097,22089,22081,22073,22065,22057,22049,22041,22033,22025,22017,22009,22001,21993,21985,21977,21969,21961,21952,21944,21936,21928,21920,21912,21904,21896,21888,21880,21872,21864,21856, -21847,21839,21831,21823,21815,21807,21799,21791,21783,21775,21767,21758,21750,21742,21734,21726,21718,21710,21702,21694,21685,21677,21669,21661,21653,21645,21637,21629,21620,21612,21604,21596,21588,21580,21572,21563,21555,21547,21539,21531,21523,21515,21506,21498,21490,21482,21474,21466,21458,21449, -21441,21433,21425,21417,21409,21400,21392,21384,21376,21368,21360,21351,21343,21335,21327,21319,21310,21302,21294,21286,21278,21270,21261,21253,21245,21237,21229,21220,21212,21204,21196,21188,21179,21171,21163,21155,21147,21138,21130,21122,21114,21106,21097,21089,21081,21073,21064,21056,21048,21040, -21032,21023,21015,21007,20999,20990,20982,20974,20966,20957,20949,20941,20933,20924,20916,20908,20900,20891,20883,20875,20867,20858,20850,20842,20834,20825,20817,20809,20801,20792,20784,20776,20768,20759,20751,20743,20735,20726,20718,20710,20701,20693,20685,20677,20668,20660,20652,20643,20635,20627, -20619,20610,20602,20594,20585,20577,20569,20561,20552,20544,20536,20527,20519,20511,20502,20494,20486,20478,20469,20461,20453,20444,20436,20428,20419,20411,20403,20394,20386,20378,20369,20361,20353,20345,20336,20328,20320,20311,20303,20295,20286,20278,20270,20261,20253,20245,20236,20228,20220,20211, -20203,20194,20186,20178,20169,20161,20153,20144,20136,20128,20119,20111,20103,20094,20086,20078,20069,20061,20052,20044,20036,20027,20019,20011,20002,19994,19986,19977,19969,19960,19952,19944,19935,19927,19919,19910,19902,19893,19885,19877,19868,19860,19852,19843,19835,19826,19818,19810,19801,19793, -19784,19776,19768,19759,19751,19742,19734,19726,19717,19709,19700,19692,19684,19675,19667,19658,19650,19642,19633,19625,19616,19608,19600,19591,19583,19574,19566,19557,19549,19541,19532,19524,19515,19507,19499,19490,19482,19473,19465,19456,19448,19440,19431,19423,19414,19406,19397,19389,19381,19372, -19364,19355,19347,19338,19330,19321,19313,19305,19296,19288,19279,19271,19262,19254,19245,19237,19229,19220,19212,19203,19195,19186,19178,19169,19161,19153,19144,19136,19127,19119,19110,19102,19093,19085,19076,19068,19059,19051,19043,19034,19026,19017,19009,19000,18992,18983,18975,18966,18958,18949, -18941,18932,18924,18915,18907,18898,18890,18882,18873,18865,18856,18848,18839,18831,18822,18814,18805,18797,18788,18780,18771,18763,18754,18746,18737,18729,18720,18712,18703,18695,18686,18678,18669,18661,18652,18644,18635,18627,18618,18610,18601,18593,18584,18576,18567,18559,18550,18542,18533,18525, -18516,18508,18499,18491,18482,18474,18465,18457,18448,18440,18431,18423,18414,18406,18397,18389,18380,18372,18363,18355,18346,18338,18329,18321,18312,18304,18295,18286,18278,18269,18261,18252,18244,18235,18227,18218,18210,18201,18193,18184,18176,18167,18159,18150,18142,18133,18124,18116,18107,18099, -18090,18082,18073,18065,18056,18048,18039,18031,18022,18014,18005,17996,17988,17979,17971,17962,17954,17945,17937,17928,17920,17911,17903,17894,17885,17877,17868,17860,17851,17843,17834,17826,17817,17809,17800,17791,17783,17774,17766,17757,17749,17740,17732,17723,17714,17706,17697,17689,17680,17672, -17663,17655,17646,17638,17629,17620,17612,17603,17595,17586,17578,17569,17561,17552,17543,17535,17526,17518,17509,17501,17492,17483,17475,17466,17458,17449,17441,17432,17424,17415,17406,17398,17389,17381,17372,17364,17355,17346,17338,17329,17321,17312,17304,17295,17287,17278,17269,17261,17252,17244, -17235,17227,17218,17209,17201,17192,17184,17175,17167,17158,17149,17141,17132,17124,17115,17107,17098,17089,17081,17072,17064,17055,17047,17038,17029,17021,17012,17004,16995,16987,16978,16969,16961,16952,16944,16935,16927,16918,16909,16901,16892,16884,16875,16867,16858,16849,16841,16832,16824,16815, -16806,16798,16789,16781,16772,16764,16755,16746,16738,16729,16721,16712,16704,16695,16686,16678,16669,16661,16652,16644,16635,16626,16618,16609,16601,16592,16583,16575,16566,16558,16549,16541,16532,16523,16515,16506,16498,16489,16481,16472,16463,16455,16446,16438,16429,16420,16412,16403,16395,16386, -16378,16369,16360,16352,16343,16335,16326,16318,16309,16300,16292,16283,16275,16266,16257,16249,16240,16232,16223,16215,16206,16197,16189,16180,16172,16163,16155,16146,16137,16129,16120,16112,16103,16094,16086,16077,16069,16060,16052,16043,16034,16026,16017,16009,16000,15992,15983,15974,15966,15957, -15949,15940,15931,15923,15914,15906,15897,15889,15880,15871,15863,15854,15846,15837,15829,15820,15811,15803,15794,15786,15777,15769,15760,15751,15743,15734,15726,15717,15709,15700,15691,15683,15674,15666,15657,15649,15640,15631,15623,15614,15606,15597,15589,15580,15571,15563,15554,15546,15537,15529, -15520,15511,15503,15494,15486,15477,15469,15460,15451,15443,15434,15426,15417,15409,15400,15392,15383,15374,15366,15357,15349,15340,15332,15323,15314,15306,15297,15289,15280,15272,15263,15255,15246,15237,15229,15220,15212,15203,15195,15186,15178,15169,15160,15152,15143,15135,15126,15118,15109,15101, -15092,15083,15075,15066,15058,15049,15041,15032,15024,15015,15006,14998,14989,14981,14972,14964,14955,14947,14938,14930,14921,14912,14904,14895,14887,14878,14870,14861,14853,14844,14836,14827,14819,14810,14801,14793,14784,14776,14767,14759,14750,14742,14733,14725,14716,14708,14699,14690,14682,14673, -14665,14656,14648,14639,14631,14622,14614,14605,14597,14588,14580,14571,14563,14554,14545,14537,14528,14520,14511,14503,14494,14486,14477,14469,14460,14452,14443,14435,14426,14418,14409,14401,14392,14384,14375,14367,14358,14350,14341,14332,14324,14315,14307,14298,14290,14281,14273,14264,14256,14247, -14239,14230,14222,14213,14205,14196,14188,14179,14171,14162,14154,14145,14137,14128,14120,14111,14103,14094,14086,14077,14069,14060,14052,14043,14035,14026,14018,14009,14001,13992,13984,13976,13967,13959,13950,13942,13933,13925,13916,13908,13899,13891,13882,13874,13865,13857,13848,13840,13831,13823, -13814,13806,13797,13789,13781,13772,13764,13755,13747,13738,13730,13721,13713,13704,13696,13687,13679,13670,13662,13654,13645,13637,13628,13620,13611,13603,13594,13586,13577,13569,13561,13552,13544,13535,13527,13518,13510,13501,13493,13485,13476,13468,13459,13451,13442,13434,13425,13417,13409,13400, -13392,13383,13375,13366,13358,13350,13341,13333,13324,13316,13307,13299,13291,13282,13274,13265,13257,13248,13240,13232,13223,13215,13206,13198,13189,13181,13173,13164,13156,13147,13139,13131,13122,13114,13105,13097,13089,13080,13072,13063,13055,13047,13038,13030,13021,13013,13005,12996,12988,12979, -12971,12963,12954,12946,12937,12929,12921,12912,12904,12896,12887,12879,12870,12862,12854,12845,12837,12828,12820,12812,12803,12795,12787,12778,12770,12762,12753,12745,12736,12728,12720,12711,12703,12695,12686,12678,12670,12661,12653,12644,12636,12628,12619,12611,12603,12594,12586,12578,12569,12561, -12553,12544,12536,12528,12519,12511,12503,12494,12486,12478,12469,12461,12453,12444,12436,12428,12419,12411,12403,12394,12386,12378,12369,12361,12353,12344,12336,12328,12320,12311,12303,12295,12286,12278,12270,12261,12253,12245,12236,12228,12220,12212,12203,12195,12187,12178,12170,12162,12154,12145, -12137,12129,12120,12112,12104,12096,12087,12079,12071,12062,12054,12046,12038,12029,12021,12013,12005,11996,11988,11980,11971,11963,11955,11947,11938,11930,11922,11914,11905,11897,11889,11881,11872,11864,11856,11848,11839,11831,11823,11815,11806,11798,11790,11782,11774,11765,11757,11749,11741,11732, -11724,11716,11708,11699,11691,11683,11675,11667,11658,11650,11642,11634,11626,11617,11609,11601,11593,11585,11576,11568,11560,11552,11544,11535,11527,11519,11511,11503,11494,11486,11478,11470,11462,11453,11445,11437,11429,11421,11413,11404,11396,11388,11380,11372,11364,11355,11347,11339,11331,11323, -11315,11306,11298,11290,11282,11274,11266,11257,11249,11241,11233,11225,11217,11209,11200,11192,11184,11176,11168,11160,11152,11144,11135,11127,11119,11111,11103,11095,11087,11079,11070,11062,11054,11046,11038,11030,11022,11014,11006,10997,10989,10981,10973,10965,10957,10949,10941,10933,10925,10916, -10908,10900,10892,10884,10876,10868,10860,10852,10844,10836,10828,10820,10811,10803,10795,10787,10779,10771,10763,10755,10747,10739,10731,10723,10715,10707,10699,10691,10683,10675,10667,10658,10650,10642,10634,10626,10618,10610,10602,10594,10586,10578,10570,10562,10554,10546,10538,10530,10522,10514, -10506,10498,10490,10482,10474,10466,10458,10450,10442,10434,10426,10418,10410,10402,10394,10386,10378,10370,10362,10354,10346,10338,10330,10322,10314,10306,10298,10290,10282,10274,10267,10259,10251,10243,10235,10227,10219,10211,10203,10195,10187,10179,10171,10163,10155,10147,10139,10131,10124,10116, -10108,10100,10092,10084,10076,10068,10060,10052,10044,10036,10029,10021,10013,10005,9997,9989,9981,9973,9965,9957,9950,9942,9934,9926,9918,9910,9902,9894,9886,9879,9871,9863,9855,9847,9839,9831,9824,9816,9808,9800,9792,9784,9776,9769,9761,9753,9745,9737,9729,9721, -9714,9706,9698,9690,9682,9674,9667,9659,9651,9643,9635,9628,9620,9612,9604,9596,9588,9581,9573,9565,9557,9549,9542,9534,9526,9518,9511,9503,9495,9487,9479,9472,9464,9456,9448,9440,9433,9425,9417,9409,9402,9394,9386,9378,9371,9363,9355,9347,9340,9332, -9324,9316,9309,9301,9293,9285,9278,9270,9262,9255,9247,9239,9231,9224,9216,9208,9201,9193,9185,9177,9170,9162,9154,9147,9139,9131,9124,9116,9108,9100,9093,9085,9077,9070,9062,9054,9047,9039,9031,9024,9016,9008,9001,8993,8985,8978,8970,8962,8955,8947, -8940,8932,8924,8917,8909,8901,8894,8886,8878,8871,8863,8856,8848,8840,8833,8825,8817,8810,8802,8795,8787,8779,8772,8764,8757,8749,8742,8734,8726,8719,8711,8704,8696,8688,8681,8673,8666,8658,8651,8643,8635,8628,8620,8613,8605,8598,8590,8583,8575,8568, -8560,8552,8545,8537,8530,8522,8515,8507,8500,8492,8485,8477,8470,8462,8455,8447,8440,8432,8425,8417,8410,8402,8395,8387,8380,8372,8365,8357,8350,8342,8335,8327,8320,8312,8305,8297,8290,8283,8275,8268,8260,8253,8245,8238,8230,8223,8216,8208,8201,8193, -8186,8178,8171,8164,8156,8149,8141,8134,8126,8119,8112,8104,8097,8089,8082,8075,8067,8060,8052,8045,8038,8030,8023,8016,8008,8001,7993,7986,7979,7971,7964,7957,7949,7942,7935,7927,7920,7913,7905,7898,7891,7883,7876,7869,7861,7854,7847,7839,7832,7825, -7817,7810,7803,7795,7788,7781,7773,7766,7759,7752,7744,7737,7730,7722,7715,7708,7701,7693,7686,7679,7671,7664,7657,7650,7642,7635,7628,7621,7613,7606,7599,7592,7584,7577,7570,7563,7556,7548,7541,7534,7527,7519,7512,7505,7498,7491,7483,7476,7469,7462, -7455,7447,7440,7433,7426,7419,7411,7404,7397,7390,7383,7376,7368,7361,7354,7347,7340,7333,7326,7318,7311,7304,7297,7290,7283,7276,7268,7261,7254,7247,7240,7233,7226,7219,7211,7204,7197,7190,7183,7176,7169,7162,7155,7148,7140,7133,7126,7119,7112,7105, -7098,7091,7084,7077,7070,7063,7056,7049,7042,7035,7027,7020,7013,7006,6999,6992,6985,6978,6971,6964,6957,6950,6943,6936,6929,6922,6915,6908,6901,6894,6887,6880,6873,6866,6859,6852,6845,6838,6831,6824,6817,6810,6803,6796,6790,6783,6776,6769,6762,6755, -6748,6741,6734,6727,6720,6713,6706,6699,6692,6685,6679,6672,6665,6658,6651,6644,6637,6630,6623,6616,6610,6603,6596,6589,6582,6575,6568,6561,6555,6548,6541,6534,6527,6520,6513,6507,6500,6493,6486,6479,6472,6466,6459,6452,6445,6438,6431,6425,6418,6411, -6404,6397,6391,6384,6377,6370,6363,6357,6350,6343,6336,6330,6323,6316,6309,6302,6296,6289,6282,6275,6269,6262,6255,6248,6242,6235,6228,6222,6215,6208,6201,6195,6188,6181,6174,6168,6161,6154,6148,6141,6134,6128,6121,6114,6108,6101,6094,6088,6081,6074, -6067,6061,6054,6048,6041,6034,6028,6021,6014,6008,6001,5994,5988,5981,5974,5968,5961,5955,5948,5941,5935,5928,5922,5915,5908,5902,5895,5889,5882,5875,5869,5862,5856,5849,5843,5836,5829,5823,5816,5810,5803,5797,5790,5784,5777,5770,5764,5757,5751,5744, -5738,5731,5725,5718,5712,5705,5699,5692,5686,5679,5673,5666,5660,5653,5647,5640,5634,5627,5621,5614,5608,5602,5595,5589,5582,5576,5569,5563,5556,5550,5544,5537,5531,5524,5518,5511,5505,5499,5492,5486,5479,5473,5467,5460,5454,5447,5441,5435,5428,5422, -5415,5409,5403,5396,5390,5384,5377,5371,5365,5358,5352,5346,5339,5333,5327,5320,5314,5308,5301,5295,5289,5282,5276,5270,5263,5257,5251,5244,5238,5232,5226,5219,5213,5207,5201,5194,5188,5182,5175,5169,5163,5157,5150,5144,5138,5132,5125,5119,5113,5107, -5101,5094,5088,5082,5076,5070,5063,5057,5051,5045,5039,5032,5026,5020,5014,5008,5001,4995,4989,4983,4977,4971,4964,4958,4952,4946,4940,4934,4928,4921,4915,4909,4903,4897,4891,4885,4879,4873,4866,4860,4854,4848,4842,4836,4830,4824,4818,4812,4806,4800, -4793,4787,4781,4775,4769,4763,4757,4751,4745,4739,4733,4727,4721,4715,4709,4703,4697,4691,4685,4679,4673,4667,4661,4655,4649,4643,4637,4631,4625,4619,4613,4607,4601,4595,4589,4583,4577,4571,4565,4559,4553,4548,4542,4536,4530,4524,4518,4512,4506,4500, -4494,4488,4482,4477,4471,4465,4459,4453,4447,4441,4435,4430,4424,4418,4412,4406,4400,4394,4389,4383,4377,4371,4365,4359,4354,4348,4342,4336,4330,4324,4319,4313,4307,4301,4295,4290,4284,4278,4272,4267,4261,4255,4249,4243,4238,4232,4226,4220,4215,4209, -4203,4198,4192,4186,4180,4175,4169,4163,4157,4152,4146,4140,4135,4129,4123,4118,4112,4106,4101,4095,4089,4083,4078,4072,4067,4061,4055,4050,4044,4038,4033,4027,4021,4016,4010,4004,3999,3993,3988,3982,3976,3971,3965,3960,3954,3948,3943,3937,3932,3926, -3921,3915,3909,3904,3898,3893,3887,3882,3876,3871,3865,3860,3854,3848,3843,3837,3832,3826,3821,3815,3810,3804,3799,3793,3788,3782,3777,3771,3766,3761,3755,3750,3744,3739,3733,3728,3722,3717,3711,3706,3701,3695,3690,3684,3679,3673,3668,3663,3657,3652, -3646,3641,3636,3630,3625,3619,3614,3609,3603,3598,3593,3587,3582,3577,3571,3566,3561,3555,3550,3545,3539,3534,3529,3523,3518,3513,3507,3502,3497,3491,3486,3481,3476,3470,3465,3460,3454,3449,3444,3439,3433,3428,3423,3418,3412,3407,3402,3397,3391,3386, -3381,3376,3371,3365,3360,3355,3350,3345,3339,3334,3329,3324,3319,3313,3308,3303,3298,3293,3288,3282,3277,3272,3267,3262,3257,3252,3247,3241,3236,3231,3226,3221,3216,3211,3206,3201,3195,3190,3185,3180,3175,3170,3165,3160,3155,3150,3145,3140,3135,3130, -3125,3119,3114,3109,3104,3099,3094,3089,3084,3079,3074,3069,3064,3059,3054,3049,3044,3039,3034,3029,3024,3020,3015,3010,3005,3000,2995,2990,2985,2980,2975,2970,2965,2960,2955,2950,2945,2941,2936,2931,2926,2921,2916,2911,2906,2901,2897,2892,2887,2882, -2877,2872,2867,2863,2858,2853,2848,2843,2838,2834,2829,2824,2819,2814,2810,2805,2800,2795,2790,2786,2781,2776,2771,2766,2762,2757,2752,2747,2743,2738,2733,2728,2724,2719,2714,2709,2705,2700,2695,2691,2686,2681,2676,2672,2667,2662,2658,2653,2648,2644, -2639,2634,2630,2625,2620,2616,2611,2606,2602,2597,2592,2588,2583,2579,2574,2569,2565,2560,2556,2551,2546,2542,2537,2533,2528,2523,2519,2514,2510,2505,2501,2496,2492,2487,2482,2478,2473,2469,2464,2460,2455,2451,2446,2442,2437,2433,2428,2424,2419,2415, -2410,2406,2401,2397,2392,2388,2383,2379,2375,2370,2366,2361,2357,2352,2348,2343,2339,2335,2330,2326,2321,2317,2313,2308,2304,2299,2295,2291,2286,2282,2278,2273,2269,2265,2260,2256,2251,2247,2243,2238,2234,2230,2226,2221,2217,2213,2208,2204,2200,2195, -2191,2187,2183,2178,2174,2170,2165,2161,2157,2153,2148,2144,2140,2136,2131,2127,2123,2119,2115,2110,2106,2102,2098,2094,2089,2085,2081,2077,2073,2068,2064,2060,2056,2052,2048,2043,2039,2035,2031,2027,2023,2019,2015,2010,2006,2002,1998,1994,1990,1986, -1982,1978,1974,1969,1965,1961,1957,1953,1949,1945,1941,1937,1933,1929,1925,1921,1917,1913,1909,1905,1901,1897,1893,1889,1885,1881,1877,1873,1869,1865,1861,1857,1853,1849,1845,1841,1837,1833,1829,1825,1821,1817,1813,1809,1806,1802,1798,1794,1790,1786, -1782,1778,1774,1770,1767,1763,1759,1755,1751,1747,1743,1740,1736,1732,1728,1724,1720,1717,1713,1709,1705,1701,1698,1694,1690,1686,1682,1679,1675,1671,1667,1663,1660,1656,1652,1648,1645,1641,1637,1633,1630,1626,1622,1619,1615,1611,1607,1604,1600,1596, -1593,1589,1585,1582,1578,1574,1571,1567,1563,1560,1556,1552,1549,1545,1541,1538,1534,1530,1527,1523,1520,1516,1512,1509,1505,1502,1498,1494,1491,1487,1484,1480,1477,1473,1470,1466,1462,1459,1455,1452,1448,1445,1441,1438,1434,1431,1427,1424,1420,1417, -1413,1410,1406,1403,1399,1396,1392,1389,1385,1382,1379,1375,1372,1368,1365,1361,1358,1355,1351,1348,1344,1341,1338,1334,1331,1327,1324,1321,1317,1314,1310,1307,1304,1300,1297,1294,1290,1287,1284,1280,1277,1274,1270,1267,1264,1261,1257,1254,1251,1247, -1244,1241,1238,1234,1231,1228,1224,1221,1218,1215,1211,1208,1205,1202,1199,1195,1192,1189,1186,1183,1179,1176,1173,1170,1167,1163,1160,1157,1154,1151,1148,1144,1141,1138,1135,1132,1129,1126,1122,1119,1116,1113,1110,1107,1104,1101,1098,1095,1091,1088, -1085,1082,1079,1076,1073,1070,1067,1064,1061,1058,1055,1052,1049,1046,1043,1040,1037,1034,1031,1028,1025,1022,1019,1016,1013,1010,1007,1004,1001,998,995,992,989,986,983,980,978,975,972,969,966,963,960,957,954,951,949,946,943,940, -937,934,931,928,926,923,920,917,914,911,909,906,903,900,897,895,892,889,886,883,881,878,875,872,870,867,864,861,859,856,853,850,848,845,842,840,837,834,831,829,826,823,821,818,815,813,810,807,805,802, -799,797,794,791,789,786,784,781,778,776,773,771,768,765,763,760,758,755,752,750,747,745,742,740,737,735,732,729,727,724,722,719,717,714,712,709,707,704,702,699,697,694,692,690,687,685,682,680,677,675, -672,670,668,665,663,660,658,655,653,651,648,646,643,641,639,636,634,632,629,627,625,622,620,618,615,613,611,608,606,604,601,599,597,594,592,590,588,585,583,581,579,576,574,572,570,567,565,563,561,558, -556,554,552,550,547,545,543,541,539,536,534,532,530,528,526,523,521,519,517,515,513,511,508,506,504,502,500,498,496,494,492,490,487,485,483,481,479,477,475,473,471,469,467,465,463,461,459,457,455,453, -451,449,447,445,443,441,439,437,435,433,431,429,427,425,423,421,419,417,415,414,412,410,408,406,404,402,400,398,397,395,393,391,389,387,385,383,382,380,378,376,374,373,371,369,367,365,363,362,360,358, -356,355,353,351,349,347,346,344,342,340,339,337,335,334,332,330,328,327,325,323,322,320,318,317,315,313,312,310,308,307,305,303,302,300,298,297,295,293,292,290,289,287,285,284,282,281,279,278,276,274, -273,271,270,268,267,265,264,262,261,259,257,256,254,253,251,250,248,247,245,244,243,241,240,238,237,235,234,232,231,229,228,227,225,224,222,221,220,218,217,215,214,213,211,210,209,207,206,204,203,202, -200,199,198,196,195,194,192,191,190,189,187,186,185,183,182,181,180,178,177,176,175,173,172,171,170,168,167,166,165,163,162,161,160,159,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140, -139,138,137,136,135,134,132,131,130,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,92,91,90, -89,88,87,86,85,84,84,83,82,81,80,79,78,78,77,76,75,74,74,73,72,71,70,70,69,68,67,66,66,65,64,63,63,62,61,60,60,59,58,57,57,56,55,55,54,53,53,52,51,51, -50,49,49,48,47,47,46,45,45,44,43,43,42,42,41,40,40,39,39,38,37,37,36,36,35,35,34,33,33,32,32,31,31,30,30,29,29,28,28,27,27,26,26,25,25,24,24,23,23,22, -22,22,21,21,20,20,19,19,19,18,18,17,17,17,16,16,15,15,15,14,14,14,13,13,13,12,12,12,11,11,11,10,10,10,9,9,9,9,8,8,8,8,7,7,7,7,6,6,6,6, -5,5,5,5,5,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -#endif \ No newline at end of file From ae58f8eb6408189eb16e3e977c58fecf21248437 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 20 Apr 2020 13:28:07 +0100 Subject: [PATCH 047/119] Don't square the threshold value. --- FMCTCSSRX.cpp | 2 +- SerialPort.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 728cc51..d6a529a 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -102,7 +102,7 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) if (m_coeff == 0.0F) return 4U; - m_threshold = float32_t(threshold * threshold); + m_threshold = float32_t(threshold); return 0U; } diff --git a/SerialPort.cpp b/SerialPort.cpp index 45624da..1490c8c 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200418 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200420 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 046a1de51af086b0e56b913357fa217a96c137f1 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 20 Apr 2020 13:54:11 +0100 Subject: [PATCH 048/119] Reduce the size of the main FM filter. --- FM.cpp | 24 ++++++++---------------- FM.h | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/FM.cpp b/FM.cpp index ee060b1..17e6462 100644 --- a/FM.cpp +++ b/FM.cpp @@ -21,23 +21,15 @@ #include "FM.h" q15_t FILTER_COEFFS[] = { - -4, 0, 1, -2, -9, -15, -16, -11, -2, 4, 6, 1, -7, -15, - -17, -9, 3, 16, 21, 14, 0, -12, -14, -1, 21, 43, 51, 41, - 19, -1, -5, 14, 50, 85, 98, 81, 44, 9, 0, 28, 80, 130, - 147, 120, 60, 2, -17, 16, 88, 157, 178, 132, 39, -52, -90, -49, - 46, 140, 166, 96, -42, -182, -245, -195, -63, 67, 106, 7, -194, -399, - -496, -429, -238, -41, 26, -106, -396, -697, -843, -743, -444, -121, 12, -165, - -603, -1084, -1329, -1163, -629, -6, 320, 67, -759, -1803, -2474, -2204, -739, 1695, - 4421, 6556, 7363, 6556, 4421, 1695, -739, -2204, -2474, -1803, -759, 67, 320, -6, - -629, -1163, -1329, -1084, -603, -165, 12, -121, -444, -743, -843, -697, -396, -106, - 26, -41, -238, -429, -496, -399, -194, 7, 106, 67, -63, -195, -245, -182, - -42, 96, 166, 140, 46, -49, -90, -52, 39, 132, 178, 157, 88, 16, - -17, 2, 60, 120, 147, 130, 80, 28, 0, 9, 44, 81, 98, 85, - 50, 14, -5, -1, 19, 41, 51, 43, 21, -1, -14, -12, 0, 14, - 21, 16, 3, -9, -17, -15, -7, 1, 6, 4, -2, -11, -16, -15, - -9, -2, 1, 0, -4}; + 26, 19, 6, -7, -13, -7, 8, 27, 35, 22, -10, -48, -70, -60, -21, 24, + 41, 3, -84, -185, -243, -222, -129, -23, 15, -66, -257, -471, -591, -539, -332, -94, + 9, -134, -503, -926, -1160, -1035, -570, -5, 300, 64, -728, -1747, -2418, -2171, -732, 1688, + 4417, 6564, 7376, 6564, 4417, 1688, -732, -2171, -2418, -1747, -728, 64, 300, -5, -570, -1035, + -1160, -926, -503, -134, 9, -94, -332, -539, -591, -471, -257, -66, 15, -23, -129, -222, + -243, -185, -84, 3, 41, 24, -21, -60, -70, -48, -10, 22, 35, 27, 8, -7, + -13, -7, 6, 19, 26}; -const uint16_t FILTER_COEFFS_LEN = 201U; +const uint16_t FILTER_COEFFS_LEN = 101U; CFM::CFM() : m_filter(), diff --git a/FM.h b/FM.h index 26d18d7..963f29b 100644 --- a/FM.h +++ b/FM.h @@ -53,7 +53,7 @@ public: private: arm_fir_instance_q15 m_filter; - q15_t m_filterState[230U]; // NoTaps + BlockSize - 1, 201 + 20 - 1 plus some spare + q15_t m_filterState[130U]; // NoTaps + BlockSize - 1, 101 + 20 - 1 plus some spare CFMKeyer m_callsign; CFMKeyer m_rfAck; CFMCTCSSRX m_ctcssRX; From 0a51b4c34eff6ef46c0e58bbdfbb6c7a93666d04 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 20 Apr 2020 21:57:21 +0100 Subject: [PATCH 049/119] Store FM output in a ring buffer. --- FM.cpp | 24 +++++++++++-- FM.h | 2 ++ FMRB.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ FMRB.h | 70 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 FMRB.cpp create mode 100644 FMRB.h diff --git a/FM.cpp b/FM.cpp index 17e6462..1385c5b 100644 --- a/FM.cpp +++ b/FM.cpp @@ -31,6 +31,8 @@ q15_t FILTER_COEFFS[] = { const uint16_t FILTER_COEFFS_LEN = 101U; +const uint16_t OUTPUT_BUFFER_SIZE = 1000U; + CFM::CFM() : m_filter(), m_filterState(), @@ -48,7 +50,8 @@ m_holdoffTimer(), m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), -m_hangTimer() +m_hangTimer(), +m_ringBuffer(OUTPUT_BUFFER_SIZE) { ::memset(m_filterState, 0x00U, 230U * sizeof(q15_t)); @@ -86,11 +89,28 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) m_ctcssTX.getAudio(output, length); - io.write(STATE_FM, output, length); + for (uint8_t i = 0U; i < length; i++) { + bool ret = m_ringBuffer.put(output[i]); + if (!ret) { + DEBUG1("Overflow in the FM ring buffer"); + break; + } + } } void CFM::process() { + uint16_t space = io.getSpace(); + uint16_t data = m_ringBuffer.getData(); + if (data < space) + space = data; + + for (uint16_t i = 0U; i < space; i++) { + q15_t sample; + m_ringBuffer.get(sample); + + io.write(STATE_FM, &sample, 1U); + } } void CFM::reset() diff --git a/FM.h b/FM.h index 963f29b..54ca54c 100644 --- a/FM.h +++ b/FM.h @@ -26,6 +26,7 @@ #include "FMTimeout.h" #include "FMKeyer.h" #include "FMTimer.h" +#include "FMRB.h" enum FM_STATE { FS_LISTENING, @@ -69,6 +70,7 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; + CFMRB m_ringBuffer; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/FMRB.cpp b/FMRB.cpp new file mode 100644 index 0000000..7f46195 --- /dev/null +++ b/FMRB.cpp @@ -0,0 +1,102 @@ +/* +TX fifo control - Copyright (C) KI6ZUM 2015 +Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +Boston, MA 02110-1301, USA. +*/ + +#include "FMRB.h" + +CFMRB::CFMRB(uint16_t length) : +m_length(length), +m_head(0U), +m_tail(0U), +m_full(false), +m_overflow(false) +{ + m_samples = new q15_t[length]; +} + +uint16_t CFMRB::getSpace() const +{ + uint16_t n = 0U; + + if (m_tail == m_head) + n = m_full ? 0U : m_length; + else if (m_tail < m_head) + n = m_length - m_head + m_tail; + else + n = m_tail - m_head; + + if (n > m_length) + n = 0U; + + return n; +} + +uint16_t CFMRB::getData() const +{ + if (m_tail == m_head) + return m_full ? m_length : 0U; + else if (m_tail < m_head) + return m_head - m_tail; + else + return m_length - m_tail + m_head; +} + +bool CFMRB::put(q15_t sample) +{ + if (m_full) { + m_overflow = true; + return false; + } + + m_samples[m_head] = sample; + + m_head++; + if (m_head >= m_length) + m_head = 0U; + + if (m_head == m_tail) + m_full = true; + + return true; +} + +bool CFMRB::get(q15_t& sample) +{ + if (m_head == m_tail && !m_full) + return false; + + sample = m_samples[m_tail]; + + m_full = false; + + m_tail++; + if (m_tail >= m_length) + m_tail = 0U; + + return true; +} + +bool CFMRB::hasOverflowed() +{ + bool overflow = m_overflow; + + m_overflow = false; + + return overflow; +} diff --git a/FMRB.h b/FMRB.h new file mode 100644 index 0000000..3d641cf --- /dev/null +++ b/FMRB.h @@ -0,0 +1,70 @@ +/* +Serial fifo control - Copyright (C) KI6ZUM 2015 +Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +Boston, MA 02110-1301, USA. +*/ + +#if !defined(FMRB_H) +#define FMRB_H + +#if defined(STM32F4XX) +#include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" +#include +#else +#include +#endif + +#if defined(__SAM3X8E__) || defined(STM32F105xC) +#define ARM_MATH_CM3 +#elif defined(STM32F7XX) +#define ARM_MATH_CM7 +#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define ARM_MATH_CM4 +#else +#error "Unknown processor type" +#endif + +#include + +class CFMRB { +public: + CFMRB(uint16_t length); + + uint16_t getSpace() const; + + uint16_t getData() const; + + bool put(q15_t sample); + + bool get(q15_t& sample); + + bool hasOverflowed(); + +private: + uint16_t m_length; + volatile q15_t* m_samples; + volatile uint16_t m_head; + volatile uint16_t m_tail; + volatile bool m_full; + bool m_overflow; +}; + +#endif From 7befef2f4a690761dfd569931e8f4f6e6226a40e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 18:41:27 +0200 Subject: [PATCH 050/119] Avoid looping samples 2 times, convert each sample on the fly --- FMCTCSSRX.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index d6a529a..de69ff5 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,18 +109,19 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) { - float32_t data[RX_BLOCK_SIZE]; - ::arm_q15_to_float(samples, data, length); + //float32_t data[RX_BLOCK_SIZE]; + //::arm_q15_to_float(samples, data, length); for (unsigned int i = 0U; i < length; i++) { float32_t q2 = m_q1; m_q1 = m_q0; - m_q0 = m_coeff * m_q1 - q2 + data[i]; + m_q0 = m_coeff * m_q1 - q2 + float32_t(samples[i]) / 32768.0F; m_count++; if (m_count == N) { float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; m_result = value >= m_threshold; + DEBUG4("CTCSS value / threshold / result", value, m_threshold, m_result); m_count = 0U; m_q0 = 0.0F; m_q1 = 0.0F; From 87c3b57ae4e259f0d04de4d2bda91b87d0eb0672 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 18:45:11 +0200 Subject: [PATCH 051/119] Stripping ut stuff, testing --- FM.cpp | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1385c5b..1b71aa5 100644 --- a/FM.cpp +++ b/FM.cpp @@ -64,18 +64,18 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) { bool validCTCSS = m_ctcssRX.process(samples, length); - stateMachine(validCTCSS && cos, length); + stateMachine(validCTCSS /*&& cos*/, length); if (m_modemState != STATE_FM) return; // Only let audio through when relaying audio - if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { - for (uint8_t i = 0U; i < length; i++) - samples[i] = 0; - } + // if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { + // for (uint8_t i = 0U; i < length; i++) + // samples[i] = 0; + // } - if (!m_callsign.isRunning()) + /*if (!m_callsign.isRunning()) m_rfAck.getAudio(samples, length); if (!m_rfAck.isRunning()) @@ -87,30 +87,32 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) q15_t output[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_filter, samples, output, length); - m_ctcssTX.getAudio(output, length); + m_ctcssTX.getAudio(output, length);*/ - for (uint8_t i = 0U; i < length; i++) { - bool ret = m_ringBuffer.put(output[i]); - if (!ret) { - DEBUG1("Overflow in the FM ring buffer"); - break; - } - } + io.write(STATE_FM, samples, length); + + // for (uint8_t i = 0U; i < length; i++) { + // bool ret = m_ringBuffer.put(output[i]); + // if (!ret) { + // DEBUG1("Overflow in the FM ring buffer"); + // break; + // } + // } } void CFM::process() { - uint16_t space = io.getSpace(); - uint16_t data = m_ringBuffer.getData(); - if (data < space) - space = data; + // uint16_t space = io.getSpace(); + // uint16_t data = m_ringBuffer.getData(); + // if (data < space) + // space = data; - for (uint16_t i = 0U; i < space; i++) { - q15_t sample; - m_ringBuffer.get(sample); + // for (uint16_t i = 0U; i < space; i++) { + // q15_t sample; + // m_ringBuffer.get(sample); - io.write(STATE_FM, &sample, 1U); - } + // io.write(STATE_FM, &sample, 1U); + // } } void CFM::reset() From 8d340cbfba5bc134abded4ca34ad5c439e4d1507 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 20:38:02 +0200 Subject: [PATCH 052/119] (almost) One loop to rule all the samples Avoid looping over and over the samples. One --- FM.cpp | 55 ++++++++++++++++----------------------------------- FM.h | 1 - FMCTCSSRX.cpp | 5 +---- FMCTCSSTX.cpp | 9 +++++++++ FMCTCSSTX.h | 1 + FMKeyer.cpp | 26 ++++++++++++++++++++++++ FMKeyer.h | 2 ++ FMTimeout.cpp | 20 +++++++++++++++++++ FMTimeout.h | 2 ++ 9 files changed, 78 insertions(+), 43 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1b71aa5..37d73c1 100644 --- a/FM.cpp +++ b/FM.cpp @@ -31,7 +31,6 @@ q15_t FILTER_COEFFS[] = { const uint16_t FILTER_COEFFS_LEN = 101U; -const uint16_t OUTPUT_BUFFER_SIZE = 1000U; CFM::CFM() : m_filter(), @@ -50,8 +49,7 @@ m_holdoffTimer(), m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), -m_hangTimer(), -m_ringBuffer(OUTPUT_BUFFER_SIZE) +m_hangTimer() { ::memset(m_filterState, 0x00U, 230U * sizeof(q15_t)); @@ -69,50 +67,31 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) if (m_modemState != STATE_FM) return; - // Only let audio through when relaying audio - // if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { - // for (uint8_t i = 0U; i < length; i++) - // samples[i] = 0; - // } + q15_t currentSample; + for(uint8_t i = 0U; i < length; i++) { + // Only let audio through when relaying audio + currentSample = samples[i];//save to a local variable to avoid indirection on every access + if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { + currentSample = 0U; + } - /*if (!m_callsign.isRunning()) - m_rfAck.getAudio(samples, length); + if(!m_callsign.isRunning()) + currentSample = m_rfAck.getAudio(currentSample); + + if(!m_rfAck.isRunning()) + currentSample = m_rfAck.getAudio(currentSample); - if (!m_rfAck.isRunning()) - m_callsign.getAudio(samples, length); + if (!m_callsign.isRunning() && !m_rfAck.isRunning()) + currentSample = m_timeoutTone.getAudio(currentSample); - if (!m_callsign.isRunning() && !m_rfAck.isRunning()) - m_timeoutTone.getAudio(samples, length); - - q15_t output[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_filter, samples, output, length); - - m_ctcssTX.getAudio(output, length);*/ + samples[i] = currentSample; + } io.write(STATE_FM, samples, length); - - // for (uint8_t i = 0U; i < length; i++) { - // bool ret = m_ringBuffer.put(output[i]); - // if (!ret) { - // DEBUG1("Overflow in the FM ring buffer"); - // break; - // } - // } } void CFM::process() { - // uint16_t space = io.getSpace(); - // uint16_t data = m_ringBuffer.getData(); - // if (data < space) - // space = data; - - // for (uint16_t i = 0U; i < space; i++) { - // q15_t sample; - // m_ringBuffer.get(sample); - - // io.write(STATE_FM, &sample, 1U); - // } } void CFM::reset() diff --git a/FM.h b/FM.h index 54ca54c..96ba7de 100644 --- a/FM.h +++ b/FM.h @@ -70,7 +70,6 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; - CFMRB m_ringBuffer; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index de69ff5..84fcc3c 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,9 +109,6 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) { - //float32_t data[RX_BLOCK_SIZE]; - //::arm_q15_to_float(samples, data, length); - for (unsigned int i = 0U; i < length; i++) { float32_t q2 = m_q1; m_q1 = m_q0; @@ -121,7 +118,7 @@ bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) if (m_count == N) { float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; m_result = value >= m_threshold; - DEBUG4("CTCSS value / threshold / result", value, m_threshold, m_result); + //DEBUG4("CTCSS value / threshold / result", value, m_threshold, m_result); m_count = 0U; m_q0 = 0.0F; m_q1 = 0.0F; diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index c2fb2c7..d8c4324 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -125,3 +125,12 @@ void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) m_n = 0U; } } + +q15_t CFMCTCSSTX::getAudio(q15_t sample) +{ + sample += m_values[m_n++]; + if(m_n >= m_length) + m_n = 0U; + + return sample; +} diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index 61b404e..f8c5bf3 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -28,6 +28,7 @@ public: uint8_t setParams(uint8_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); + inline q15_t getAudio(q15_t sample); private: q15_t* m_values; diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 77da003..44225b0 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -152,6 +152,32 @@ void CFMKeyer::getAudio(q15_t* samples, uint8_t length) } } +q15_t CFMKeyer::getAudio(q15_t sample) +{ + if (!m_wanted) + return 0U; //TODO F4FXL, not sure what to do here + + q15_t output; + bool b = READ_BIT(m_poBuffer, m_poPos); + if (b) + output = sample + m_audio[m_audioPos]; + + m_audioPos++; + if (m_audioPos >= m_audioLen) + m_audioPos = 0U; + m_dotPos++; + if (m_dotPos >= m_dotLen) { + m_dotPos = 0U; + m_poPos++; + if (m_poPos >= m_poLen) { + stop(); + return sample; + } + } + + return output; +} + void CFMKeyer::start() { if (isRunning()) diff --git a/FMKeyer.h b/FMKeyer.h index 3676522..e13f2fb 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -29,6 +29,8 @@ public: void getAudio(q15_t* samples, uint8_t length); + q15_t getAudio(q15_t sample); + void start(); void stop(); diff --git a/FMTimeout.cpp b/FMTimeout.cpp index 38030f4..d6dab6e 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -62,6 +62,26 @@ void CFMTimeout::getAudio(q15_t* samples, uint8_t length) } } +q15_t CFMTimeout::getAudio(q15_t sample) +{ + if (m_pos > 12000U) { + q31_t sample = BUSY_AUDIO[m_n] * m_level; + sample = q15_t(__SSAT((sample >> 15), 16)); + + m_n++; + if (m_n >= BUSY_AUDIO_LEN) + m_n = 0U; + } else { + sample = 0U; + } + + m_pos++; + if (m_pos >= 24000U) + m_pos = 0U; + + return sample; +} + void CFMTimeout::start() { m_running = true; diff --git a/FMTimeout.h b/FMTimeout.h index 6286ddb..d6e4967 100644 --- a/FMTimeout.h +++ b/FMTimeout.h @@ -32,6 +32,8 @@ public: void getAudio(q15_t* samples, uint8_t length); + q15_t getAudio(q15_t sample); + private: q15_t m_level; bool m_running; From 04fd9838b54b561e327aba0dc0702e01c9b98b06 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 21:00:09 +0200 Subject: [PATCH 053/119] Fix state machine wrong kerchunk --- FM.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/FM.cpp b/FM.cpp index 37d73c1..b64e52e 100644 --- a/FM.cpp +++ b/FM.cpp @@ -191,23 +191,25 @@ void CFM::stateMachine(bool validSignal, uint8_t length) void CFM::listeningState(bool validSignal) { - if (m_kerchunkTimer.getTimeout() > 0U) { - DEBUG1("State to KERCHUNK"); - m_state = FS_KERCHUNK; - m_kerchunkTimer.start(); - } else { - DEBUG1("State to RELAYING"); - m_state = FS_RELAYING; - if (m_callsignAtStart) - sendCallsign(); + if(validSignal) { + if (m_kerchunkTimer.getTimeout() > 0U) { + DEBUG1("State to KERCHUNK"); + m_state = FS_KERCHUNK; + m_kerchunkTimer.start(); + } else { + DEBUG1("State to RELAYING"); + m_state = FS_RELAYING; + if (m_callsignAtStart) + sendCallsign(); + } + + beginRelaying(); + + m_callsignTimer.start(); + + DEBUG1("Change to STATE_FM"); + m_modemState = STATE_FM; } - - beginRelaying(); - - m_callsignTimer.start(); - - DEBUG1("Change to STATE_FM"); - m_modemState = STATE_FM; } void CFM::kerchunkState(bool validSignal) From 9170c398be1f990f97d0e78c1b70e63f61e3d788 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 21:36:30 +0200 Subject: [PATCH 054/119] Reenable CTCSS --- FM.cpp | 6 ++++-- FMCTCSSTX.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/FM.cpp b/FM.cpp index b64e52e..db339e6 100644 --- a/FM.cpp +++ b/FM.cpp @@ -75,14 +75,16 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample = 0U; } - if(!m_callsign.isRunning()) + /*if(!m_callsign.isRunning()) currentSample = m_rfAck.getAudio(currentSample); if(!m_rfAck.isRunning()) currentSample = m_rfAck.getAudio(currentSample); if (!m_callsign.isRunning() && !m_rfAck.isRunning()) - currentSample = m_timeoutTone.getAudio(currentSample); + currentSample = m_timeoutTone.getAudio(currentSample);*/ + + currentSample = m_ctcssTX.getAudio(currentSample); samples[i] = currentSample; } diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index f8c5bf3..e72d654 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -28,7 +28,7 @@ public: uint8_t setParams(uint8_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); - inline q15_t getAudio(q15_t sample); + q15_t getAudio(q15_t sample); private: q15_t* m_values; From 1c599138ada1610e5730dfdddad3e59a64cd95c6 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 22:00:11 +0200 Subject: [PATCH 055/119] GetAudio do not take any arguments --- FM.cpp | 10 +++++----- FMCTCSSTX.cpp | 4 ++-- FMCTCSSTX.h | 2 +- FMKeyer.cpp | 12 ++++++------ FMKeyer.h | 2 +- FMTimeout.cpp | 8 ++++++-- FMTimeout.h | 2 +- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/FM.cpp b/FM.cpp index db339e6..5852061 100644 --- a/FM.cpp +++ b/FM.cpp @@ -75,16 +75,16 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample = 0U; } - /*if(!m_callsign.isRunning()) - currentSample = m_rfAck.getAudio(currentSample); + if(!m_callsign.isRunning()) + currentSample += m_rfAck.getAudio(); if(!m_rfAck.isRunning()) - currentSample = m_rfAck.getAudio(currentSample); + currentSample += m_callsign.getAudio(); if (!m_callsign.isRunning() && !m_rfAck.isRunning()) - currentSample = m_timeoutTone.getAudio(currentSample);*/ + currentSample += m_timeoutTone.getAudio(); - currentSample = m_ctcssTX.getAudio(currentSample); + currentSample += m_ctcssTX.getAudio(); samples[i] = currentSample; } diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index d8c4324..5011ce5 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -126,9 +126,9 @@ void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) } } -q15_t CFMCTCSSTX::getAudio(q15_t sample) +q15_t CFMCTCSSTX::getAudio() { - sample += m_values[m_n++]; + q15_t sample = m_values[m_n++]; if(m_n >= m_length) m_n = 0U; diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index e72d654..a8dfd37 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -28,7 +28,7 @@ public: uint8_t setParams(uint8_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(q15_t sample); + q15_t getAudio(); private: q15_t* m_values; diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 44225b0..f16ebc0 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -152,15 +152,15 @@ void CFMKeyer::getAudio(q15_t* samples, uint8_t length) } } -q15_t CFMKeyer::getAudio(q15_t sample) +q15_t CFMKeyer::getAudio() { + q15_t output = 0U; if (!m_wanted) - return 0U; //TODO F4FXL, not sure what to do here - - q15_t output; + return 0U; + bool b = READ_BIT(m_poBuffer, m_poPos); if (b) - output = sample + m_audio[m_audioPos]; + output = m_audio[m_audioPos]; m_audioPos++; if (m_audioPos >= m_audioLen) @@ -171,7 +171,7 @@ q15_t CFMKeyer::getAudio(q15_t sample) m_poPos++; if (m_poPos >= m_poLen) { stop(); - return sample; + return output; } } diff --git a/FMKeyer.h b/FMKeyer.h index e13f2fb..88308e6 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -29,7 +29,7 @@ public: void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(q15_t sample); + q15_t getAudio(); void start(); void stop(); diff --git a/FMTimeout.cpp b/FMTimeout.cpp index d6dab6e..c64aada 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -62,8 +62,12 @@ void CFMTimeout::getAudio(q15_t* samples, uint8_t length) } } -q15_t CFMTimeout::getAudio(q15_t sample) +q15_t CFMTimeout::getAudio() { + q15_t sample = 0U; + if (!m_running) + return sample; + if (m_pos > 12000U) { q31_t sample = BUSY_AUDIO[m_n] * m_level; sample = q15_t(__SSAT((sample >> 15), 16)); @@ -79,7 +83,7 @@ q15_t CFMTimeout::getAudio(q15_t sample) if (m_pos >= 24000U) m_pos = 0U; - return sample; + return sample; } void CFMTimeout::start() diff --git a/FMTimeout.h b/FMTimeout.h index d6e4967..8b8a415 100644 --- a/FMTimeout.h +++ b/FMTimeout.h @@ -32,7 +32,7 @@ public: void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(q15_t sample); + q15_t getAudio(); private: q15_t m_level; From ae6cbcd9e761d0994837be0bfa89f5685e6ff7b3 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 22:43:59 +0200 Subject: [PATCH 056/119] Minor clean up --- FM.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 5852061..add211f 100644 --- a/FM.cpp +++ b/FM.cpp @@ -69,8 +69,9 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) q15_t currentSample; for(uint8_t i = 0U; i < length; i++) { - // Only let audio through when relaying audio currentSample = samples[i];//save to a local variable to avoid indirection on every access + + // Only let audio through when relaying audio if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { currentSample = 0U; } From 1b0229ca6c3c25f32fc259da3402f5330a630031 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 21 Apr 2020 23:03:30 +0200 Subject: [PATCH 057/119] Attemp to do filtering --- FM.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index add211f..3fedf2e 100644 --- a/FM.cpp +++ b/FM.cpp @@ -70,7 +70,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) q15_t currentSample; for(uint8_t i = 0U; i < length; i++) { currentSample = samples[i];//save to a local variable to avoid indirection on every access - + // Only let audio through when relaying audio if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { currentSample = 0U; @@ -85,6 +85,9 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); + //ToDo Filtering + //::arm_fir_fast_q15(&m_filter, samples + i, ¤tSample, 1); + currentSample += m_ctcssTX.getAudio(); samples[i] = currentSample; From 03f50562363afea609b332e87eb859c8d2c899a4 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 21 Apr 2020 22:30:28 +0100 Subject: [PATCH 058/119] Small code cleanups. --- FM.cpp | 15 ++++---- FM.h | 3 +- FMKeyer.h | 1 - FMRB.cpp | 102 ------------------------------------------------- FMRB.h | 70 --------------------------------- IO.cpp | 6 +-- SerialPort.cpp | 2 +- 7 files changed, 11 insertions(+), 188 deletions(-) delete mode 100644 FMRB.cpp delete mode 100644 FMRB.h diff --git a/FM.cpp b/FM.cpp index 3fedf2e..f3a8fa5 100644 --- a/FM.cpp +++ b/FM.cpp @@ -58,28 +58,27 @@ m_hangTimer() m_filter.pCoeffs = FILTER_COEFFS; } -void CFM::samples(bool cos, q15_t* samples, uint8_t length) +void CFM::samples(q15_t* samples, uint8_t length) { bool validCTCSS = m_ctcssRX.process(samples, length); - stateMachine(validCTCSS /*&& cos*/, length); + stateMachine(validCTCSS, length); if (m_modemState != STATE_FM) return; q15_t currentSample; - for(uint8_t i = 0U; i < length; i++) { + for (uint8_t i = 0U; i < length; i++) { currentSample = samples[i];//save to a local variable to avoid indirection on every access // Only let audio through when relaying audio - if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) { + if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) currentSample = 0U; - } - if(!m_callsign.isRunning()) + if (!m_callsign.isRunning()) currentSample += m_rfAck.getAudio(); - if(!m_rfAck.isRunning()) + if (!m_rfAck.isRunning()) currentSample += m_callsign.getAudio(); if (!m_callsign.isRunning() && !m_rfAck.isRunning()) @@ -197,7 +196,7 @@ void CFM::stateMachine(bool validSignal, uint8_t length) void CFM::listeningState(bool validSignal) { - if(validSignal) { + if (validSignal) { if (m_kerchunkTimer.getTimeout() > 0U) { DEBUG1("State to KERCHUNK"); m_state = FS_KERCHUNK; diff --git a/FM.h b/FM.h index 96ba7de..8fd4bde 100644 --- a/FM.h +++ b/FM.h @@ -26,7 +26,6 @@ #include "FMTimeout.h" #include "FMKeyer.h" #include "FMTimer.h" -#include "FMRB.h" enum FM_STATE { FS_LISTENING, @@ -42,7 +41,7 @@ class CFM { public: CFM(); - void samples(bool cos, q15_t* samples, uint8_t length); + void samples(q15_t* samples, uint8_t length); void process(); diff --git a/FMKeyer.h b/FMKeyer.h index 88308e6..2d85499 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -28,7 +28,6 @@ public: uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level); void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(); void start(); diff --git a/FMRB.cpp b/FMRB.cpp deleted file mode 100644 index 7f46195..0000000 --- a/FMRB.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* -TX fifo control - Copyright (C) KI6ZUM 2015 -Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This library 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the -Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -Boston, MA 02110-1301, USA. -*/ - -#include "FMRB.h" - -CFMRB::CFMRB(uint16_t length) : -m_length(length), -m_head(0U), -m_tail(0U), -m_full(false), -m_overflow(false) -{ - m_samples = new q15_t[length]; -} - -uint16_t CFMRB::getSpace() const -{ - uint16_t n = 0U; - - if (m_tail == m_head) - n = m_full ? 0U : m_length; - else if (m_tail < m_head) - n = m_length - m_head + m_tail; - else - n = m_tail - m_head; - - if (n > m_length) - n = 0U; - - return n; -} - -uint16_t CFMRB::getData() const -{ - if (m_tail == m_head) - return m_full ? m_length : 0U; - else if (m_tail < m_head) - return m_head - m_tail; - else - return m_length - m_tail + m_head; -} - -bool CFMRB::put(q15_t sample) -{ - if (m_full) { - m_overflow = true; - return false; - } - - m_samples[m_head] = sample; - - m_head++; - if (m_head >= m_length) - m_head = 0U; - - if (m_head == m_tail) - m_full = true; - - return true; -} - -bool CFMRB::get(q15_t& sample) -{ - if (m_head == m_tail && !m_full) - return false; - - sample = m_samples[m_tail]; - - m_full = false; - - m_tail++; - if (m_tail >= m_length) - m_tail = 0U; - - return true; -} - -bool CFMRB::hasOverflowed() -{ - bool overflow = m_overflow; - - m_overflow = false; - - return overflow; -} diff --git a/FMRB.h b/FMRB.h deleted file mode 100644 index 3d641cf..0000000 --- a/FMRB.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -Serial fifo control - Copyright (C) KI6ZUM 2015 -Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This library 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with this library; if not, write to the -Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -Boston, MA 02110-1301, USA. -*/ - -#if !defined(FMRB_H) -#define FMRB_H - -#if defined(STM32F4XX) -#include "stm32f4xx.h" -#elif defined(STM32F7XX) -#include "stm32f7xx.h" -#elif defined(STM32F105xC) -#include "stm32f1xx.h" -#include -#else -#include -#endif - -#if defined(__SAM3X8E__) || defined(STM32F105xC) -#define ARM_MATH_CM3 -#elif defined(STM32F7XX) -#define ARM_MATH_CM7 -#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) -#define ARM_MATH_CM4 -#else -#error "Unknown processor type" -#endif - -#include - -class CFMRB { -public: - CFMRB(uint16_t length); - - uint16_t getSpace() const; - - uint16_t getData() const; - - bool put(q15_t sample); - - bool get(q15_t& sample); - - bool hasOverflowed(); - -private: - uint16_t m_length; - volatile q15_t* m_samples; - volatile uint16_t m_head; - volatile uint16_t m_tail; - volatile bool m_full; - bool m_overflow; -}; - -#endif diff --git a/IO.cpp b/IO.cpp index ceae6ab..a26b6d1 100644 --- a/IO.cpp +++ b/IO.cpp @@ -370,8 +370,7 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - bool cos = getCOSInt(); - fm.samples(cos, FMVals, RX_BLOCK_SIZE); + fm.samples(FMVals, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -444,8 +443,7 @@ void CIO::process() FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); } #endif - bool cos = getCOSInt(); - fm.samples(cos, FMVals, RX_BLOCK_SIZE); + fm.samples(FMVals, RX_BLOCK_SIZE); } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); diff --git a/SerialPort.cpp b/SerialPort.cpp index 1490c8c..0d9be11 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200420 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200421 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 63847ebe626f14ad631328a05d16a75176caab41 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 22 Apr 2020 11:04:27 +0200 Subject: [PATCH 059/119] One loop to rule them all ! Change CFM::Samples and CFMCTCSSRX::process to be able to process all stuff in one and only loop. --- FM.cpp | 39 +++++++++++++++++++++++++++++++-------- FMCTCSSRX.cpp | 40 ++++++++++++++++++++++++---------------- FMCTCSSRX.h | 43 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 31 deletions(-) diff --git a/FM.cpp b/FM.cpp index f3a8fa5..a34dc61 100644 --- a/FM.cpp +++ b/FM.cpp @@ -60,17 +60,39 @@ m_hangTimer() void CFM::samples(q15_t* samples, uint8_t length) { - bool validCTCSS = m_ctcssRX.process(samples, length); - - stateMachine(validCTCSS, length); - - if (m_modemState != STATE_FM) - return; + CTCSSState ctcssState; + bool validCTCSS = false; q15_t currentSample; - for (uint8_t i = 0U; i < length; i++) { + uint8_t i = 0; + + for (; i < length; i++) { currentSample = samples[i];//save to a local variable to avoid indirection on every access + ctcssState = m_ctcssRX.process(currentSample); + + if(CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + //Not enough samples to determine if you have CTCSS, just carry on + continue; + } else if(CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { + //we had enough samples for CTCSS and we are in some other mode than FM + validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS, i + 1U); + if (m_modemState != STATE_FM) + continue; + } else if(CTCSS_READY(ctcssState) && m_modemState == STATE_FM) { + //We had enough samples for CTCSS and we are in FM mode, trigger the state machine + validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS, i + 1U); + if (m_modemState != STATE_FM) + break; + } else if(CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { + //Not enough samples for CTCSS but we already are in FM, trigger the state machine + //but do not trigger the state machine on every single sample, save CPU! + validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS, i + 1U); + } + // Only let audio through when relaying audio if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) currentSample = 0U; @@ -92,7 +114,8 @@ void CFM::samples(q15_t* samples, uint8_t length) samples[i] = currentSample; } - io.write(STATE_FM, samples, length); + if(m_modemState == STATE_FM) + io.write(STATE_FM, samples, i);//only write the actual number of processed samples to IO } void CFM::process() diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 84fcc3c..7f24333 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -86,7 +86,7 @@ m_threshold(0.0F), m_count(0U), m_q0(0.0F), m_q1(0.0F), -m_result(false) +m_result((CTCSSState)0U) { } @@ -107,31 +107,39 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) return 0U; } -bool CFMCTCSSRX::process(q15_t* samples, uint8_t length) +CTCSSState CFMCTCSSRX::process(q15_t sample) { - for (unsigned int i = 0U; i < length; i++) { - float32_t q2 = m_q1; - m_q1 = m_q0; - m_q0 = m_coeff * m_q1 - q2 + float32_t(samples[i]) / 32768.0F; + m_result = m_result & (~CTS_READY); + float32_t q2 = m_q1; + m_q1 = m_q0; + m_q0 = m_coeff * m_q1 - q2 + float32_t(sample) / 32768.0F; - m_count++; - if (m_count == N) { - float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; - m_result = value >= m_threshold; - //DEBUG4("CTCSS value / threshold / result", value, m_threshold, m_result); - m_count = 0U; - m_q0 = 0.0F; - m_q1 = 0.0F; - } + m_count++; + if (m_count == N) { + float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; + m_result = m_result | CTS_READY; + if(value >= m_threshold) + m_result = m_result | CTS_VALID; + else + m_result = m_result & ~CTS_VALID; + + m_count = 0U; + m_q0 = 0.0F; + m_q1 = 0.0F; } return m_result; } +CTCSSState CFMCTCSSRX::getState() +{ + return m_result; +} + void CFMCTCSSRX::reset() { m_q0 = 0.0F; m_q1 = 0.0F; - m_result = false; + m_result = (CTCSSState)0U; m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index b1d7c02..5238d7b 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -21,23 +21,52 @@ #include "Config.h" +enum CTCSSState : uint8_t +{ + CTS_READY = 1, + CTS_VALID = 2, + CTS_READY_VALID = CTS_READY | CTS_VALID +}; + +inline CTCSSState operator|(CTCSSState a, CTCSSState b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline CTCSSState operator&(CTCSSState a, CTCSSState b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline CTCSSState operator~(CTCSSState a) +{ + return static_cast(~(static_cast(a))); +} + +#define CTCSS_READY(a) ((a & CTS_READY) != 0) +#define CTCSS_NOT_READY(a) ((a & CTS_READY) == 0) +#define CTCSS_VALID(a) ((a & CTS_VALID) != 0) +#define CTCSS_NOT_VALID(a) ((a & CTS_VALID) == 0) + class CFMCTCSSRX { public: CFMCTCSSRX(); uint8_t setParams(uint8_t frequency, uint8_t threshold); + + CTCSSState process(q15_t samples); - bool process(q15_t* samples, uint8_t length); + CTCSSState getState(); void reset(); private: - float32_t m_coeff; - float32_t m_threshold; - uint16_t m_count; - float32_t m_q0; - float32_t m_q1; - bool m_result; + float32_t m_coeff; + float32_t m_threshold; + uint16_t m_count; + float32_t m_q0; + float32_t m_q1; + CTCSSState m_result; }; #endif From 3f1f1d15869e967ef4436c9a01fa5d42ef9364fc Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 22 Apr 2020 14:08:34 +0100 Subject: [PATCH 060/119] Small tidy-ups. --- FM.cpp | 25 ++++++++++--------------- FMCTCSSRX.cpp | 6 +++--- FMCTCSSRX.h | 3 ++- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/FM.cpp b/FM.cpp index a34dc61..1bda3cb 100644 --- a/FM.cpp +++ b/FM.cpp @@ -60,36 +60,31 @@ m_hangTimer() void CFM::samples(q15_t* samples, uint8_t length) { - CTCSSState ctcssState; - - bool validCTCSS = false; - q15_t currentSample; uint8_t i = 0; - for (; i < length; i++) { - currentSample = samples[i];//save to a local variable to avoid indirection on every access + q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access - ctcssState = m_ctcssRX.process(currentSample); + CTCSSState ctcssState = m_ctcssRX.process(currentSample); - if(CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { + if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { //Not enough samples to determine if you have CTCSS, just carry on continue; - } else if(CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { + } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { //we had enough samples for CTCSS and we are in some other mode than FM - validCTCSS = CTCSS_VALID(ctcssState); + bool validCTCSS = CTCSS_VALID(ctcssState); stateMachine(validCTCSS, i + 1U); if (m_modemState != STATE_FM) continue; - } else if(CTCSS_READY(ctcssState) && m_modemState == STATE_FM) { + } else if (CTCSS_READY(ctcssState) && m_modemState == STATE_FM) { //We had enough samples for CTCSS and we are in FM mode, trigger the state machine - validCTCSS = CTCSS_VALID(ctcssState); + bool validCTCSS = CTCSS_VALID(ctcssState); stateMachine(validCTCSS, i + 1U); if (m_modemState != STATE_FM) break; - } else if(CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { + } else if (CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { //Not enough samples for CTCSS but we already are in FM, trigger the state machine //but do not trigger the state machine on every single sample, save CPU! - validCTCSS = CTCSS_VALID(ctcssState); + bool validCTCSS = CTCSS_VALID(ctcssState); stateMachine(validCTCSS, i + 1U); } @@ -114,7 +109,7 @@ void CFM::samples(q15_t* samples, uint8_t length) samples[i] = currentSample; } - if(m_modemState == STATE_FM) + if (m_modemState == STATE_FM) io.write(STATE_FM, samples, i);//only write the actual number of processed samples to IO } diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 7f24333..620ae4c 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -86,7 +86,7 @@ m_threshold(0.0F), m_count(0U), m_q0(0.0F), m_q1(0.0F), -m_result((CTCSSState)0U) +m_result(CTS_NONE) { } @@ -118,7 +118,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) if (m_count == N) { float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; m_result = m_result | CTS_READY; - if(value >= m_threshold) + if (value >= m_threshold) m_result = m_result | CTS_VALID; else m_result = m_result & ~CTS_VALID; @@ -140,6 +140,6 @@ void CFMCTCSSRX::reset() { m_q0 = 0.0F; m_q1 = 0.0F; - m_result = (CTCSSState)0U; + m_result = CTS_NONE; m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 5238d7b..496b963 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -21,8 +21,9 @@ #include "Config.h" -enum CTCSSState : uint8_t +enum CTCSSState { + CTS_NONE = 0, CTS_READY = 1, CTS_VALID = 2, CTS_READY_VALID = CTS_READY | CTS_VALID From f936bc6fe83159a69f1647b8d25a5ba0b0fb77c8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 22 Apr 2020 14:15:07 +0100 Subject: [PATCH 061/119] Use a shorter filter. --- FM.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1bda3cb..aa484c3 100644 --- a/FM.cpp +++ b/FM.cpp @@ -21,15 +21,12 @@ #include "FM.h" q15_t FILTER_COEFFS[] = { - 26, 19, 6, -7, -13, -7, 8, 27, 35, 22, -10, -48, -70, -60, -21, 24, - 41, 3, -84, -185, -243, -222, -129, -23, 15, -66, -257, -471, -591, -539, -332, -94, - 9, -134, -503, -926, -1160, -1035, -570, -5, 300, 64, -728, -1747, -2418, -2171, -732, 1688, - 4417, 6564, 7376, 6564, 4417, 1688, -732, -2171, -2418, -1747, -728, 64, 300, -5, -570, -1035, - -1160, -926, -503, -134, 9, -94, -332, -539, -591, -471, -257, -66, 15, -23, -129, -222, - -243, -185, -84, 3, 41, 24, -21, -60, -70, -48, -10, 22, 35, 27, 8, -7, - -13, -7, 6, 19, 26}; + -630, -842, -846, -634, -312, -53, -14, -251, -683, -1113, -1322, -1179, -718, -147, 234, 172, + -399, -1298, -2124, -2402, -1783, -201, 2051, 4399, 6169, 6827, 6169, 4399, 2051, -201, -1783, -2402, + -2124, -1298, -399, 172, 234, -147, -718, -1179, -1322, -1113, -683, -251, -14, -53, -312, -634, + -846, -842, -630}; -const uint16_t FILTER_COEFFS_LEN = 101U; +const uint16_t FILTER_COEFFS_LEN = 51U; CFM::CFM() : From 57730d7f81fd2316cf0f3a6f4e5848589da8a597 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 22 Apr 2020 14:37:23 +0100 Subject: [PATCH 062/119] Implement an efficient FIR filter. --- FM.cpp | 36 +++++++++++++++++++++++++++--------- FM.h | 6 ++++-- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/FM.cpp b/FM.cpp index aa484c3..9073649 100644 --- a/FM.cpp +++ b/FM.cpp @@ -30,8 +30,8 @@ const uint16_t FILTER_COEFFS_LEN = 51U; CFM::CFM() : -m_filter(), -m_filterState(), +m_filterBuffer(NULL), +m_filterPosition(0U), m_callsign(), m_rfAck(), m_ctcssRX(), @@ -48,11 +48,7 @@ m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer() { - ::memset(m_filterState, 0x00U, 230U * sizeof(q15_t)); - - m_filter.numTaps = FILTER_COEFFS_LEN; - m_filter.pState = m_filterState; - m_filter.pCoeffs = FILTER_COEFFS; + m_filterBuffer = new q15_t[FILTER_COEFFS_LEN]; } void CFM::samples(q15_t* samples, uint8_t length) @@ -98,8 +94,7 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - //ToDo Filtering - //::arm_fir_fast_q15(&m_filter, samples + i, ¤tSample, 1); + currentSample = filter(currentSample); currentSample += m_ctcssTX.getAudio(); @@ -395,3 +390,26 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } + +q15_t CFM::filter(q15_t sample) +{ + q15_t output = 0; + + m_filterBuffer[m_filterPosition] = sample; + + uint8_t iTaps = 0U; + + for (int8_t i = m_filterPosition; i >= 0; i--) { + q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; + output += q15_t(__SSAT((temp >> 15), 16)); + } + + for (int8_t i = FILTER_COEFFS_LEN - 1; i >= m_filterPosition; i--) { + q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; + output += q15_t(__SSAT((temp >> 15), 16)); + } + + m_filterPosition = (m_filterPosition + 1U) % FILTER_COEFFS_LEN; + + return output; +} diff --git a/FM.h b/FM.h index 8fd4bde..b348ee9 100644 --- a/FM.h +++ b/FM.h @@ -52,8 +52,8 @@ public: uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: - arm_fir_instance_q15 m_filter; - q15_t m_filterState[130U]; // NoTaps + BlockSize - 1, 101 + 20 - 1 plus some spare + q15_t* m_filterBuffer; + uint8_t m_filterPosition; CFMKeyer m_callsign; CFMKeyer m_rfAck; CFMCTCSSRX m_ctcssRX; @@ -81,6 +81,8 @@ private: void sendCallsign(); void beginRelaying(); + + q15_t filter(q15_t sample); }; #endif From 526a53cd8ceff52269ee14b71b689b73ab0e93f7 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 22 Apr 2020 17:40:01 +0100 Subject: [PATCH 063/119] Use fixed point for the Goertzel algorithm. --- FMCTCSSRX.cpp | 157 +++++++++++++++++++++++++++++--------------------- FMCTCSSRX.h | 10 ++-- 2 files changed, 96 insertions(+), 71 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 620ae4c..bdf210e 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -21,59 +21,59 @@ #include "FMCTCSSRX.h" const struct CTCSS_TABLE { - uint8_t frequency; - float32_t coeff; + uint8_t frequency; + q31_t coeffDivTwo; } CTCSS_TABLE_DATA[] = { - { 67U, 1.999692F}, - { 69U, 1.999671F}, - { 71U, 1.999646F}, - { 74U, 1.999621F}, - { 77U, 1.999594F}, - { 79U, 1.999565F}, - { 82U, 1.999534F}, - { 85U, 1.999500F}, - { 88U, 1.999463F}, - { 91U, 1.999426F}, - { 94U, 1.999384F}, - { 97U, 1.999350F}, - {100U, 1.999315F}, - {103U, 1.999266F}, - {107U, 1.999212F}, - {110U, 1.999157F}, - {114U, 1.999097F}, - {118U, 1.999033F}, - {123U, 1.998963F}, - {127U, 1.998889F}, - {131U, 1.998810F}, - {136U, 1.998723F}, - {141U, 1.998632F}, - {146U, 1.998535F}, - {151U, 1.998429F}, - {156U, 1.998317F}, - {159U, 1.998250F}, - {162U, 1.998197F}, - {165U, 1.998123F}, - {167U, 1.998068F}, - {171U, 1.997989F}, - {173U, 1.997930F}, - {177U, 1.997846F}, - {179U, 1.997782F}, - {183U, 1.997693F}, - {186U, 1.997624F}, - {189U, 1.997529F}, - {192U, 1.997453F}, - {196U, 1.997351F}, - {199U, 1.997273F}, - {203U, 1.997162F}, - {206U, 1.997078F}, - {210U, 1.996958F}, - {218U, 1.996741F}, - {225U, 1.996510F}, - {229U, 1.996404F}, - {233U, 1.996261F}, - {241U, 1.995994F}, - {250U, 1.995708F}, - {254U, 1.995576F}}; + { 67U, 2147153298}, + { 69U, 2147130228}, + { 71U, 2147103212}, + { 74U, 2147076297}, + { 77U, 2147047330}, + { 79U, 2147016195}, + { 82U, 2146982775}, + { 85U, 2146946945}, + { 88U, 2146907275}, + { 91U, 2146867538}, + { 94U, 2146822298}, + { 97U, 2146785526}, + {100U, 2146747759}, + {103U, 2146695349}, + {107U, 2146637984}, + {110U, 2146578604}, + {114U, 2146513835}, + {118U, 2146445080}, + {123U, 2146370355}, + {127U, 2146291161}, + {131U, 2146205372}, + {136U, 2146112589}, + {141U, 2146014479}, + {146U, 2145910829}, + {151U, 2145796971}, + {156U, 2145676831}, + {159U, 2145604646}, + {162U, 2145547790}, + {165U, 2145468230}, + {167U, 2145409363}, + {171U, 2145324517}, + {173U, 2145261046}, + {177U, 2145170643}, + {179U, 2145102321}, + {183U, 2145006080}, + {186U, 2144932648}, + {189U, 2144830280}, + {192U, 2144748638}, + {196U, 2144639788}, + {199U, 2144555290}, + {203U, 2144436713}, + {206U, 2144346237}, + {210U, 2144217348}, + {218U, 2143983951}, + {225U, 2143735870}, + {229U, 2143622139}, + {233U, 2143469001}, + {241U, 2143182299}, + {250U, 2142874683}, + {254U, 2142733729}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; @@ -81,11 +81,11 @@ const uint8_t CTCSS_TABLE_DATA_LEN = 50U; const uint16_t N = 24000U / 4U; CFMCTCSSRX::CFMCTCSSRX() : -m_coeff(0.0F), -m_threshold(0.0F), +m_coeffDivTwo(0), +m_threshold(0), m_count(0U), -m_q0(0.0F), -m_q1(0.0F), +m_q0(0), +m_q1(0), m_result(CTS_NONE) { } @@ -94,15 +94,15 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { if (CTCSS_TABLE_DATA[i].frequency == frequency) { - m_coeff = CTCSS_TABLE_DATA[i].coeff; + m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; break; } } - if (m_coeff == 0.0F) + if (m_coeffDivTwo == 0) return 4U; - m_threshold = float32_t(threshold); + m_threshold = q31_t(threshold); return 0U; } @@ -110,13 +110,38 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); - float32_t q2 = m_q1; + + q31_t q2 = m_q1; m_q1 = m_q0; - m_q0 = m_coeff * m_q1 - q2 + float32_t(sample) / 32768.0F; + + // Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1 + q63_t t1 = m_coeffDivTwo * m_q1; + q31_t t2 = __SSAT((t1 >> 32), 31); + q31_t t3 = t2 * 2; + + // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample + m_q0 = t3 - q2 + q31_t(sample); m_count++; if (m_count == N) { - float32_t value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeff; + // Q31 multiplication, t2 = m_q0 * m_q0 + q63_t t1 = m_q0 * m_q0; + q31_t t2 = __SSAT((t1 >> 32), 31); + + // Q31 multiplication, t4 = m_q0 * m_q0 + q63_t t3 = m_q1 * m_q1; + q31_t t4 = __SSAT((t3 >> 32), 31); + + // Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2 + q63_t t5 = m_q0 * m_q1; + q31_t t6 = __SSAT((t5 >> 32), 31); + q63_t t7 = t6 * m_coeffDivTwo; + q31_t t8 = __SSAT((t7 >> 32), 31); + q31_t t9 = t8 * 2; + + // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 + q31_t value = t2 + t4 - t9; + m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; @@ -124,8 +149,8 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) m_result = m_result & ~CTS_VALID; m_count = 0U; - m_q0 = 0.0F; - m_q1 = 0.0F; + m_q0 = 0; + m_q1 = 0; } return m_result; @@ -138,8 +163,8 @@ CTCSSState CFMCTCSSRX::getState() void CFMCTCSSRX::reset() { - m_q0 = 0.0F; - m_q1 = 0.0F; + m_q0 = 0; + m_q1 = 0; m_result = CTS_NONE; m_count = 0U; } diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 496b963..52c95e7 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -62,11 +62,11 @@ public: void reset(); private: - float32_t m_coeff; - float32_t m_threshold; - uint16_t m_count; - float32_t m_q0; - float32_t m_q1; + q31_t m_coeffDivTwo; + q31_t m_threshold; + uint16_t m_count; + q31_t m_q0; + q31_t m_q1; CTCSSState m_result; }; From f19009b1328d8ca7a58c7372847d37a041d180a6 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 22 Apr 2020 22:10:34 +0100 Subject: [PATCH 064/119] Allow for two levels of the callsign. --- FM.cpp | 16 +++++++---- FM.h | 2 +- FMCTCSSTX.cpp | 11 -------- FMCTCSSTX.h | 1 - FMKeyer.cpp | 74 ++++++++++++++++++++++++++------------------------ FMKeyer.h | 10 ++++--- FMTimeout.cpp | 23 ---------------- FMTimeout.h | 2 -- SerialPort.cpp | 13 +++++---- 9 files changed, 63 insertions(+), 89 deletions(-) diff --git a/FM.cpp b/FM.cpp index 9073649..d94c5a4 100644 --- a/FM.cpp +++ b/FM.cpp @@ -86,10 +86,14 @@ void CFM::samples(q15_t* samples, uint8_t length) currentSample = 0U; if (!m_callsign.isRunning()) - currentSample += m_rfAck.getAudio(); + currentSample += m_rfAck.getHighAudio(); - if (!m_rfAck.isRunning()) - currentSample += m_callsign.getAudio(); + if (!m_rfAck.isRunning()) { + if (m_state == FS_LISTENING) + currentSample += m_callsign.getHighAudio(); + else + currentSample += m_callsign.getLowAudio(); + } if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); @@ -114,7 +118,7 @@ void CFM::reset() m_ctcssRX.reset(); } -uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t level, bool callsignAtStart, bool callsignAtEnd) +uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd) { m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; @@ -127,7 +131,7 @@ uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency m_holdoffTimer.setTimeout(holdoffTime, 0U); m_callsignTimer.setTimeout(callsignTime, 0U); - return m_callsign.setParams(callsign, speed, frequency, level); + return m_callsign.setParams(callsign, speed, frequency, highLevel, lowLevel); } uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level) @@ -135,7 +139,7 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ m_ackDelayTimer.setTimeout(0U, delay); m_ackMinTimer.setTimeout(minTime, 0U); - return m_rfAck.setParams(rfAck, speed, frequency, level); + return m_rfAck.setParams(rfAck, speed, frequency, level, level); } uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) diff --git a/FM.h b/FM.h index b348ee9..d2f9419 100644 --- a/FM.h +++ b/FM.h @@ -47,7 +47,7 @@ public: void reset(); - uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t level, bool callsignAtStart, bool callsignAtEnd); + uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 5011ce5..fb5a997 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -115,17 +115,6 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) return 0U; } -void CFMCTCSSTX::getAudio(q15_t* samples, uint8_t length) -{ - for (uint8_t i = 0U; i < length; i++) { - samples[i] += m_values[m_n]; - - m_n++; - if (m_n >= m_length) - m_n = 0U; - } -} - q15_t CFMCTCSSTX::getAudio() { q15_t sample = m_values[m_n++]; diff --git a/FMCTCSSTX.h b/FMCTCSSTX.h index a8dfd37..0c3e6e5 100644 --- a/FMCTCSSTX.h +++ b/FMCTCSSTX.h @@ -27,7 +27,6 @@ public: uint8_t setParams(uint8_t frequency, uint8_t level); - void getAudio(q15_t* samples, uint8_t length); q15_t getAudio(); private: diff --git a/FMKeyer.cpp b/FMKeyer.cpp index f16ebc0..70beea4 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -84,11 +84,13 @@ m_dotLen(0U), m_dotPos(0U), m_audio(NULL), m_audioLen(0U), -m_audioPos(0U) +m_audioPos(0U), +m_highLevel(0U), +m_lowLevel(0) { } -uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level) +uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel) { for (uint8_t i = 0U; text[i] != '\0'; i++) { for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { @@ -109,58 +111,60 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, } } - q15_t value = q15_t(level * 128); + m_highLevel = q15_t(highLevel * 128); + m_lowLevel = q15_t(lowLevel * 128); m_dotLen = 24000U / speed; // In samples m_audioLen = 24000U / frequency; // In samples - m_audio = new q15_t[m_audioLen]; + m_audio = new bool[m_audioLen]; for (uint16_t i = 0U; i < m_audioLen; i++) { if (i < (m_audioLen / 2U)) - m_audio[i] = value; + m_audio[i] = true; else - m_audio[i] = -value; + m_audio[i] = false; } return 0U; } -void CFMKeyer::getAudio(q15_t* samples, uint8_t length) -{ - if (!m_wanted) - return; - - for (uint8_t i = 0U; i < length; i++) { - bool b = READ_BIT(m_poBuffer, m_poPos); - if (b) - samples[i] += m_audio[m_audioPos]; - - m_audioPos++; - if (m_audioPos >= m_audioLen) - m_audioPos = 0U; - m_dotPos++; - if (m_dotPos >= m_dotLen) { - m_dotPos = 0U; - m_poPos++; - if (m_poPos >= m_poLen) { - stop(); - return; - } - } - } -} - -q15_t CFMKeyer::getAudio() +q15_t CFMKeyer::getHighAudio() { q15_t output = 0U; if (!m_wanted) - return 0U; - + return 0U; + bool b = READ_BIT(m_poBuffer, m_poPos); if (b) - output = m_audio[m_audioPos]; + output = m_audio[m_audioPos] ? m_highLevel : -m_highLevel; + + m_audioPos++; + if (m_audioPos >= m_audioLen) + m_audioPos = 0U; + m_dotPos++; + if (m_dotPos >= m_dotLen) { + m_dotPos = 0U; + m_poPos++; + if (m_poPos >= m_poLen) { + stop(); + return output; + } + } + + return output; +} + +q15_t CFMKeyer::getLowAudio() +{ + q15_t output = 0U; + if (!m_wanted) + return 0U; + + bool b = READ_BIT(m_poBuffer, m_poPos); + if (b) + output = m_audio[m_audioPos] ? m_lowLevel : -m_lowLevel; m_audioPos++; if (m_audioPos >= m_audioLen) diff --git a/FMKeyer.h b/FMKeyer.h index 2d85499..799e85c 100644 --- a/FMKeyer.h +++ b/FMKeyer.h @@ -25,10 +25,10 @@ class CFMKeyer { public: CFMKeyer(); - uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t level); + uint8_t setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel); - void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(); + q15_t getHighAudio(); + q15_t getLowAudio(); void start(); void stop(); @@ -42,9 +42,11 @@ private: uint16_t m_poPos; uint16_t m_dotLen; uint16_t m_dotPos; - q15_t* m_audio; + bool* m_audio; uint16_t m_audioLen; uint16_t m_audioPos; + q15_t m_highLevel; + q15_t m_lowLevel; }; #endif diff --git a/FMTimeout.cpp b/FMTimeout.cpp index c64aada..893e53b 100644 --- a/FMTimeout.cpp +++ b/FMTimeout.cpp @@ -39,29 +39,6 @@ void CFMTimeout::setParams(uint8_t level) m_level = q15_t(level * 128); } -void CFMTimeout::getAudio(q15_t* samples, uint8_t length) -{ - if (!m_running) - return; - - for (uint8_t i = 0U; i < length; i++) { - if (m_pos > 12000U) { - q31_t sample = BUSY_AUDIO[m_n] * m_level; - samples[i] = q15_t(__SSAT((sample >> 15), 16)); - - m_n++; - if (m_n >= BUSY_AUDIO_LEN) - m_n = 0U; - } else { - samples[i] = 0; - } - - m_pos++; - if (m_pos >= 24000U) - m_pos = 0U; - } -} - q15_t CFMTimeout::getAudio() { q15_t sample = 0U; diff --git a/FMTimeout.h b/FMTimeout.h index 8b8a415..b2f747c 100644 --- a/FMTimeout.h +++ b/FMTimeout.h @@ -30,8 +30,6 @@ public: void start(); void stop(); - void getAudio(q15_t* samples, uint8_t length); - q15_t getAudio(); private: diff --git a/SerialPort.cpp b/SerialPort.cpp index 0d9be11..121ee68 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -365,25 +365,26 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams1(const uint8_t* data, uint8_t length) { - if (length < 7U) + 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 level = data[4U]; + uint8_t highLevel = data[4U]; + uint8_t lowLevel = data[5U]; - bool callAtStart = (data[5U] & 0x01U) == 0x01U; - bool callAtEnd = (data[5U] & 0x02U) == 0x02U; + bool callAtStart = (data[6U] & 0x01U) == 0x01U; + bool callAtEnd = (data[6U] & 0x02U) == 0x02U; char callsign[50U]; uint8_t n = 0U; - for (uint8_t i = 6U; i < length; i++, n++) + for (uint8_t i = 7U; i < length; i++, n++) callsign[n] = data[i]; callsign[n] = '\0'; - return fm.setCallsign(callsign, speed, frequency, time, holdoff, level, callAtStart, callAtEnd); + return fm.setCallsign(callsign, speed, frequency, time, holdoff, highLevel, lowLevel, callAtStart, callAtEnd); } uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) From bf5ad25609171f04e02ecbc8346912f0c69c9299 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 23 Apr 2020 12:36:34 +0100 Subject: [PATCH 065/119] A working fixed point Goertzel based on simulations. --- FMCTCSSRX.cpp | 24 +++++++++++++----------- FMCTCSSRX.h | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index bdf210e..3305ba8 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -22,7 +22,7 @@ const struct CTCSS_TABLE { uint8_t frequency; - q31_t coeffDivTwo; + q63_t coeffDivTwo; } CTCSS_TABLE_DATA[] = { { 67U, 2147153298}, { 69U, 2147130228}, @@ -111,32 +111,34 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); + q31_t samp = q31_t(sample) << 16; + q31_t q2 = m_q1; m_q1 = m_q0; // Q31 multiplication, t3 = m_coeffDivTwo * 2 * m_q1 q63_t t1 = m_coeffDivTwo * m_q1; - q31_t t2 = __SSAT((t1 >> 32), 31); + q31_t t2 = __SSAT((t1 >> 31), 31); q31_t t3 = t2 * 2; - // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + q31_t(sample); + // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + samp + m_q0 = t3 - q2 + samp; m_count++; if (m_count == N) { // Q31 multiplication, t2 = m_q0 * m_q0 - q63_t t1 = m_q0 * m_q0; - q31_t t2 = __SSAT((t1 >> 32), 31); + q63_t t1 = q63_t(m_q0) * q63_t(m_q0); + q31_t t2 = __SSAT((t1 >> 31), 31); // Q31 multiplication, t4 = m_q0 * m_q0 - q63_t t3 = m_q1 * m_q1; - q31_t t4 = __SSAT((t3 >> 32), 31); + q63_t t3 = q63_t(m_q1) * q63_t(m_q1); + q31_t t4 = __SSAT((t3 >> 31), 31); // Q31 multiplication, t9 = m_q0 * m_q1 * m_coeffDivTwo * 2 - q63_t t5 = m_q0 * m_q1; - q31_t t6 = __SSAT((t5 >> 32), 31); + q63_t t5 = q63_t(m_q0) * q63_t(m_q1); + q31_t t6 = __SSAT((t5 >> 31), 31); q63_t t7 = t6 * m_coeffDivTwo; - q31_t t8 = __SSAT((t7 >> 32), 31); + q31_t t8 = __SSAT((t7 >> 31), 31); q31_t t9 = t8 * 2; // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 52c95e7..8d2adaa 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -62,7 +62,7 @@ public: void reset(); private: - q31_t m_coeffDivTwo; + q63_t m_coeffDivTwo; q31_t m_threshold; uint16_t m_count; q31_t m_q0; From 906de3ad119cdc1effd4acafad04d94735a0cbc4 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 21:16:20 +0200 Subject: [PATCH 066/119] Remove bit roation --- FMCTCSSRX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 3305ba8..623fc5b 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -111,7 +111,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); - q31_t samp = q31_t(sample) << 16; + q31_t samp = q31_t(sample); q31_t q2 = m_q1; m_q1 = m_q0; From ce0aebce488b9c595c3f7601285c448d53b63014 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 21:16:52 +0200 Subject: [PATCH 067/119] Using 2 stage IIR filter --- FM.cpp | 45 +++++++++------------------------------------ FM.h | 7 +++++-- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/FM.cpp b/FM.cpp index d94c5a4..1c70b40 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,18 +20,11 @@ #include "Globals.h" #include "FM.h" -q15_t FILTER_COEFFS[] = { - -630, -842, -846, -634, -312, -53, -14, -251, -683, -1113, -1322, -1179, -718, -147, 234, 172, - -399, -1298, -2124, -2402, -1783, -201, 2051, 4399, 6169, 6827, 6169, 4399, 2051, -201, -1783, -2402, - -2124, -1298, -399, 172, 234, -147, -718, -1179, -1322, -1113, -683, -251, -14, -53, -312, -634, - -846, -842, -630}; - -const uint16_t FILTER_COEFFS_LEN = 51U; - +// 2 stage IIR Butterworth filter generated using https://github.com/F4FXL/iir_fixed_point/blob/4f1e580a7dad9f8742d24a06edd14b62110ba6e4/gen_coeff.py +q15_t FILTER_COEFFS[] = {1105, 2210, 1105, 16384,-19003,7512, 14,//1st stage + 16384,-32768,16384,16384,-31020,14751,14};//2nd stage CFM::CFM() : -m_filterBuffer(NULL), -m_filterPosition(0U), m_callsign(), m_rfAck(), m_ctcssRX(), @@ -46,13 +39,15 @@ m_holdoffTimer(), m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), -m_hangTimer() +m_hangTimer(), +m_filter() { - m_filterBuffer = new q15_t[FILTER_COEFFS_LEN]; + arm_biquad_cascade_df1_init_q15(&m_filter, 2, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) { + arm_biquad_casd_df1_inst_q15* filterPtr = &m_filter; uint8_t i = 0; for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access @@ -98,7 +93,8 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - currentSample = filter(currentSample); + //currentSample = filter(currentSample); + arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); currentSample += m_ctcssTX.getAudio(); @@ -394,26 +390,3 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } - -q15_t CFM::filter(q15_t sample) -{ - q15_t output = 0; - - m_filterBuffer[m_filterPosition] = sample; - - uint8_t iTaps = 0U; - - for (int8_t i = m_filterPosition; i >= 0; i--) { - q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; - output += q15_t(__SSAT((temp >> 15), 16)); - } - - for (int8_t i = FILTER_COEFFS_LEN - 1; i >= m_filterPosition; i--) { - q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; - output += q15_t(__SSAT((temp >> 15), 16)); - } - - m_filterPosition = (m_filterPosition + 1U) % FILTER_COEFFS_LEN; - - return output; -} diff --git a/FM.h b/FM.h index d2f9419..a125a87 100644 --- a/FM.h +++ b/FM.h @@ -37,6 +37,9 @@ enum FM_STATE { FS_HANG }; + + + class CFM { public: CFM(); @@ -52,8 +55,6 @@ public: uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: - q15_t* m_filterBuffer; - uint8_t m_filterPosition; CFMKeyer m_callsign; CFMKeyer m_rfAck; CFMCTCSSRX m_ctcssRX; @@ -69,6 +70,8 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; + arm_biquad_casd_df1_inst_q15 m_filter; + q15_t m_filterState[8];//must be filterOrder * 4 long void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From 7a75db2aebb69008df3cb80c41818233fec79f06 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 23 Apr 2020 21:30:41 +0100 Subject: [PATCH 068/119] Remove the bit shift of the sample. --- FMCTCSSRX.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 3305ba8..5c36f66 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -111,8 +111,6 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); - q31_t samp = q31_t(sample) << 16; - q31_t q2 = m_q1; m_q1 = m_q0; @@ -121,8 +119,8 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t2 = __SSAT((t1 >> 31), 31); q31_t t3 = t2 * 2; - // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + samp - m_q0 = t3 - q2 + samp; + // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample + m_q0 = t3 - q2 + q31_t(sample); m_count++; if (m_count == N) { From 507a4bf64c496e0e61f43b3308e7e1700f13cbe4 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 22:37:29 +0200 Subject: [PATCH 069/119] 3rd order IIR --- FM.cpp | 13 ++++++++----- FM.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1c70b40..1ec448b 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,9 +20,13 @@ #include "Globals.h" #include "FM.h" -// 2 stage IIR Butterworth filter generated using https://github.com/F4FXL/iir_fixed_point/blob/4f1e580a7dad9f8742d24a06edd14b62110ba6e4/gen_coeff.py -q15_t FILTER_COEFFS[] = {1105, 2210, 1105, 16384,-19003,7512, 14,//1st stage - 16384,-32768,16384,16384,-31020,14751,14};//2nd stage +// 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below +// 0.2db band pass ripple +// 300 - 2700Hz +q15_t FILTER_COEFFS[] = { 362, 724, 362,16384,-18947,10676, 14,//1st stage + 16384, 0, -16384,16384,-25170, 9526, 14,//2nd stage + 16384,-32768, 16384,16384,-32037,15730,14};//3rd stage + CFM::CFM() : m_callsign(), @@ -42,7 +46,7 @@ m_ackDelayTimer(), m_hangTimer(), m_filter() { - arm_biquad_cascade_df1_init_q15(&m_filter, 2, FILTER_COEFFS, m_filterState, 0); + arm_biquad_cascade_df1_init_q15(&m_filter, 3, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) @@ -93,7 +97,6 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - //currentSample = filter(currentSample); arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); currentSample += m_ctcssTX.getAudio(); diff --git a/FM.h b/FM.h index a125a87..474f36d 100644 --- a/FM.h +++ b/FM.h @@ -71,7 +71,7 @@ private: CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; arm_biquad_casd_df1_inst_q15 m_filter; - q15_t m_filterState[8];//must be filterOrder * 4 long + q15_t m_filterState[12];//must be filterOrder * 4 long void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From f8d082e2ddfff0358f9255504cd7f56b8f1d15a5 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 24 Apr 2020 09:10:50 +0200 Subject: [PATCH 070/119] Add FM filtering --- FM.cpp | 14 ++-- FM.h | 6 +- FMDirectForm1.h | 114 ++++++++++++++++++++++++++++++++ FMGenerateFilterCoefficients.py | 45 +++++++++++++ 4 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 FMDirectForm1.h create mode 100644 FMGenerateFilterCoefficients.py diff --git a/FM.cpp b/FM.cpp index 1ec448b..03aa6ec 100644 --- a/FM.cpp +++ b/FM.cpp @@ -23,9 +23,9 @@ // 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below // 0.2db band pass ripple // 300 - 2700Hz -q15_t FILTER_COEFFS[] = { 362, 724, 362,16384,-18947,10676, 14,//1st stage - 16384, 0, -16384,16384,-25170, 9526, 14,//2nd stage - 16384,-32768, 16384,16384,-32037,15730,14};//3rd stage +q15_t FILTER_COEFFS[] = {362,724,362,16384,-18947,10676, + 16384,0,-16384,16384,-25170,9526, + 16384,-32768,16384,16384,-32037,15730}; CFM::CFM() : @@ -44,14 +44,14 @@ m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer(), -m_filter() +m_filterStage1( 724, 1448, 724, 32768, -37895, 21352), +m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), +m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460) { - arm_biquad_cascade_df1_init_q15(&m_filter, 3, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) { - arm_biquad_casd_df1_inst_q15* filterPtr = &m_filter; uint8_t i = 0; for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access @@ -97,7 +97,7 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); + currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)))); currentSample += m_ctcssTX.getAudio(); diff --git a/FM.h b/FM.h index 474f36d..77a3d81 100644 --- a/FM.h +++ b/FM.h @@ -26,6 +26,7 @@ #include "FMTimeout.h" #include "FMKeyer.h" #include "FMTimer.h" +#include "FMDirectForm1.h" enum FM_STATE { FS_LISTENING, @@ -70,8 +71,9 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; - arm_biquad_casd_df1_inst_q15 m_filter; - q15_t m_filterState[12];//must be filterOrder * 4 long + CFMDirectFormI m_filterStage1; + CFMDirectFormI m_filterStage2; + CFMDirectFormI m_filterStage3; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/FMDirectForm1.h b/FMDirectForm1.h new file mode 100644 index 0000000..857e0a6 --- /dev/null +++ b/FMDirectForm1.h @@ -0,0 +1,114 @@ + +/******************************************************************************* +This header file has been taken from: +"A Collection of Useful C++ Classes for Digital Signal Processing" +By Vinnie Falco +Bernd Porr adapted it for Linux and turned it into a filter using +fixed point arithmetic. +-------------------------------------------------------------------------------- +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vinnie Falco +Copyright (C) 2013-2017, Bernd Porr, mail@berndporr.me.uk +Copyright (C) 2020 , Mario Molitor , mario_molitor@web.de +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*******************************************************************************/ + +// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h + +#include "Globals.h" + +#ifndef DIRECTFORMI_HPP_ +#define DIRECTFORMI_HPP_ +class CFMDirectFormI +{ +public: + + // convenience function which takes the a0 argument but ignores it! + CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2, + const q31_t, const q31_t a1, const q31_t a2) + { + // FIR coefficients + c_b0 = b0; + c_b1 = b1; + c_b2 = b2; + // IIR coefficients + c_a1 = a1; + c_a2 = a2; + reset(); + } + + CFMDirectFormI(const CFMDirectFormI &my) + { + // delay line + m_x2 = my.m_x2; // x[n-2] + m_y2 = my.m_y2; // y[n-2] + m_x1 = my.m_x1; // x[n-1] + m_y1 = my.m_y1; // y[n-1] + + // coefficients + c_b0 = my.c_b0; + c_b1 = my.c_b1; + c_b2 = my.c_b2; // FIR + c_a1 = my.c_a1; + c_a2 = my.c_a2; // IIR + + // scaling factor + q_scaling = my.q_scaling; // 2^q_scaling + } + + void reset () + { + m_x1 = 0; + m_x2 = 0; + m_y1 = 0; + m_y2 = 0; + } + + // filtering operation: one sample in and one out + inline q15_t filter(const q15_t in) + { + // calculate the output + register q31_t out_upscaled = c_b0 * in //F4FXL puting stauration here made everything quiet, not sure why + + c_b1 * m_x1 + + c_b2 * m_x2 + - c_a1 * m_y1 + - c_a2 * m_y2; + + q15_t out = __SSAT(out_upscaled >> 15, 15); + + // update the delay lines + m_x2 = m_x1; + m_y2 = m_y1; + m_x1 = in; + m_y1 = out; + + return out; + } + +private: + // delay line + q31_t m_x2; // x[n-2] + q31_t m_y2; // y[n-2] + q31_t m_x1; // x[n-1] + q31_t m_y1; // y[n-1] + + // coefficients + q31_t c_b0,c_b1,c_b2; // FIR + q31_t c_a1,c_a2; // IIR +}; + +#endif /* DIRECTFORMI_HPP_ */ \ No newline at end of file diff --git a/FMGenerateFilterCoefficients.py b/FMGenerateFilterCoefficients.py new file mode 100644 index 0000000..84ea62b --- /dev/null +++ b/FMGenerateFilterCoefficients.py @@ -0,0 +1,45 @@ +# based on https://github.com/berndporr/iir_fixed_point/blob/master/gen_coeff.py + +import numpy as np +import scipy.signal as signal +import pylab as pl + +# Calculate the coefficients for a pure fixed point +# integer filter + +# sampling rate +fs = 24000 + +# cutoffs +f1 = 300 +f2 = 2700 +# ripple +rp = 0.2 + +# scaling factor in bits, do not change ! +q = 15 +# scaling factor as facor... +scaling_factor = 2**q + +# let's generate a sequence of 2nd order IIR filters +#sos = signal.butter(2,[f1/fs*2,f2/fs*2],'pass',output='sos') +sos = signal.cheby1(3,rp,[f1/fs*2,f2/fs*2],'bandpass', output='sos') + +sos = np.round((sos) * scaling_factor) + +# print coefficients +for biquad in sos: + for coeff in biquad: + print(int(coeff),",",sep="",end="") + #print((coeff),",",sep="",end="") + print("") + +# plot the frequency response +b,a = signal.sos2tf(sos) +w,h = signal.freqz(b,a) +pl.plot(w/np.pi/2*fs,20*np.log(np.abs(h))) +pl.xlabel('frequency/Hz'); +pl.ylabel('gain/dB'); +pl.ylim(top=1,bottom=-20); +pl.xlim(left=250, right=12000); +pl.show() \ No newline at end of file From a720ceeb54ba6f39f4dff700791b5afed8ace2db Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 24 Apr 2020 09:20:26 +0200 Subject: [PATCH 071/119] clean up --- FM.cpp | 8 -------- FMDirectForm1.h | 9 +++------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/FM.cpp b/FM.cpp index 03aa6ec..019e27e 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,14 +20,6 @@ #include "Globals.h" #include "FM.h" -// 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below -// 0.2db band pass ripple -// 300 - 2700Hz -q15_t FILTER_COEFFS[] = {362,724,362,16384,-18947,10676, - 16384,0,-16384,16384,-25170,9526, - 16384,-32768,16384,16384,-32037,15730}; - - CFM::CFM() : m_callsign(), m_rfAck(), diff --git a/FMDirectForm1.h b/FMDirectForm1.h index 857e0a6..4b90245 100644 --- a/FMDirectForm1.h +++ b/FMDirectForm1.h @@ -31,8 +31,8 @@ THE SOFTWARE. #include "Globals.h" -#ifndef DIRECTFORMI_HPP_ -#define DIRECTFORMI_HPP_ +#ifndef DIRECTFORMI_H_ +#define DIRECTFORMI_H_ class CFMDirectFormI { public: @@ -65,9 +65,6 @@ public: c_b2 = my.c_b2; // FIR c_a1 = my.c_a1; c_a2 = my.c_a2; // IIR - - // scaling factor - q_scaling = my.q_scaling; // 2^q_scaling } void reset () @@ -111,4 +108,4 @@ private: q31_t c_a1,c_a2; // IIR }; -#endif /* DIRECTFORMI_HPP_ */ \ No newline at end of file +#endif /* DIRECTFORMI_H */ \ No newline at end of file From 22c40e1d968c4ddff03cd25fc588df4aa0a8bc08 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 21:16:20 +0200 Subject: [PATCH 072/119] Remove bit roation --- FMCTCSSRX.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 5c36f66..6240f34 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -111,6 +111,8 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); + q31_t samp = q31_t(sample); + q31_t q2 = m_q1; m_q1 = m_q0; From 8ecd8b67f40997e5bbbb31599653a03f9376c5ae Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 21:16:52 +0200 Subject: [PATCH 073/119] Using 2 stage IIR filter --- FM.cpp | 45 +++++++++------------------------------------ FM.h | 7 +++++-- 2 files changed, 14 insertions(+), 38 deletions(-) diff --git a/FM.cpp b/FM.cpp index d94c5a4..1c70b40 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,18 +20,11 @@ #include "Globals.h" #include "FM.h" -q15_t FILTER_COEFFS[] = { - -630, -842, -846, -634, -312, -53, -14, -251, -683, -1113, -1322, -1179, -718, -147, 234, 172, - -399, -1298, -2124, -2402, -1783, -201, 2051, 4399, 6169, 6827, 6169, 4399, 2051, -201, -1783, -2402, - -2124, -1298, -399, 172, 234, -147, -718, -1179, -1322, -1113, -683, -251, -14, -53, -312, -634, - -846, -842, -630}; - -const uint16_t FILTER_COEFFS_LEN = 51U; - +// 2 stage IIR Butterworth filter generated using https://github.com/F4FXL/iir_fixed_point/blob/4f1e580a7dad9f8742d24a06edd14b62110ba6e4/gen_coeff.py +q15_t FILTER_COEFFS[] = {1105, 2210, 1105, 16384,-19003,7512, 14,//1st stage + 16384,-32768,16384,16384,-31020,14751,14};//2nd stage CFM::CFM() : -m_filterBuffer(NULL), -m_filterPosition(0U), m_callsign(), m_rfAck(), m_ctcssRX(), @@ -46,13 +39,15 @@ m_holdoffTimer(), m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), -m_hangTimer() +m_hangTimer(), +m_filter() { - m_filterBuffer = new q15_t[FILTER_COEFFS_LEN]; + arm_biquad_cascade_df1_init_q15(&m_filter, 2, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) { + arm_biquad_casd_df1_inst_q15* filterPtr = &m_filter; uint8_t i = 0; for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access @@ -98,7 +93,8 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - currentSample = filter(currentSample); + //currentSample = filter(currentSample); + arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); currentSample += m_ctcssTX.getAudio(); @@ -394,26 +390,3 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } - -q15_t CFM::filter(q15_t sample) -{ - q15_t output = 0; - - m_filterBuffer[m_filterPosition] = sample; - - uint8_t iTaps = 0U; - - for (int8_t i = m_filterPosition; i >= 0; i--) { - q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; - output += q15_t(__SSAT((temp >> 15), 16)); - } - - for (int8_t i = FILTER_COEFFS_LEN - 1; i >= m_filterPosition; i--) { - q31_t temp = FILTER_COEFFS[iTaps++] * m_filterBuffer[i]; - output += q15_t(__SSAT((temp >> 15), 16)); - } - - m_filterPosition = (m_filterPosition + 1U) % FILTER_COEFFS_LEN; - - return output; -} diff --git a/FM.h b/FM.h index d2f9419..a125a87 100644 --- a/FM.h +++ b/FM.h @@ -37,6 +37,9 @@ enum FM_STATE { FS_HANG }; + + + class CFM { public: CFM(); @@ -52,8 +55,6 @@ public: uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); private: - q15_t* m_filterBuffer; - uint8_t m_filterPosition; CFMKeyer m_callsign; CFMKeyer m_rfAck; CFMCTCSSRX m_ctcssRX; @@ -69,6 +70,8 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; + arm_biquad_casd_df1_inst_q15 m_filter; + q15_t m_filterState[8];//must be filterOrder * 4 long void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From 4f9d5a8c70d9a8c84563471cd0fe5adae0fa226e Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Thu, 23 Apr 2020 22:37:29 +0200 Subject: [PATCH 074/119] 3rd order IIR --- FM.cpp | 13 ++++++++----- FM.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 1c70b40..1ec448b 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,9 +20,13 @@ #include "Globals.h" #include "FM.h" -// 2 stage IIR Butterworth filter generated using https://github.com/F4FXL/iir_fixed_point/blob/4f1e580a7dad9f8742d24a06edd14b62110ba6e4/gen_coeff.py -q15_t FILTER_COEFFS[] = {1105, 2210, 1105, 16384,-19003,7512, 14,//1st stage - 16384,-32768,16384,16384,-31020,14751,14};//2nd stage +// 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below +// 0.2db band pass ripple +// 300 - 2700Hz +q15_t FILTER_COEFFS[] = { 362, 724, 362,16384,-18947,10676, 14,//1st stage + 16384, 0, -16384,16384,-25170, 9526, 14,//2nd stage + 16384,-32768, 16384,16384,-32037,15730,14};//3rd stage + CFM::CFM() : m_callsign(), @@ -42,7 +46,7 @@ m_ackDelayTimer(), m_hangTimer(), m_filter() { - arm_biquad_cascade_df1_init_q15(&m_filter, 2, FILTER_COEFFS, m_filterState, 0); + arm_biquad_cascade_df1_init_q15(&m_filter, 3, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) @@ -93,7 +97,6 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - //currentSample = filter(currentSample); arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); currentSample += m_ctcssTX.getAudio(); diff --git a/FM.h b/FM.h index a125a87..474f36d 100644 --- a/FM.h +++ b/FM.h @@ -71,7 +71,7 @@ private: CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; arm_biquad_casd_df1_inst_q15 m_filter; - q15_t m_filterState[8];//must be filterOrder * 4 long + q15_t m_filterState[12];//must be filterOrder * 4 long void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From 5c2659deaa21cdb22797ca553cab53ea14910d0b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 24 Apr 2020 09:10:50 +0200 Subject: [PATCH 075/119] Add FM filtering --- FM.cpp | 14 ++-- FM.h | 6 +- FMDirectForm1.h | 114 ++++++++++++++++++++++++++++++++ FMGenerateFilterCoefficients.py | 45 +++++++++++++ 4 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 FMDirectForm1.h create mode 100644 FMGenerateFilterCoefficients.py diff --git a/FM.cpp b/FM.cpp index 1ec448b..03aa6ec 100644 --- a/FM.cpp +++ b/FM.cpp @@ -23,9 +23,9 @@ // 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below // 0.2db band pass ripple // 300 - 2700Hz -q15_t FILTER_COEFFS[] = { 362, 724, 362,16384,-18947,10676, 14,//1st stage - 16384, 0, -16384,16384,-25170, 9526, 14,//2nd stage - 16384,-32768, 16384,16384,-32037,15730,14};//3rd stage +q15_t FILTER_COEFFS[] = {362,724,362,16384,-18947,10676, + 16384,0,-16384,16384,-25170,9526, + 16384,-32768,16384,16384,-32037,15730}; CFM::CFM() : @@ -44,14 +44,14 @@ m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer(), -m_filter() +m_filterStage1( 724, 1448, 724, 32768, -37895, 21352), +m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), +m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460) { - arm_biquad_cascade_df1_init_q15(&m_filter, 3, FILTER_COEFFS, m_filterState, 0); } void CFM::samples(q15_t* samples, uint8_t length) { - arm_biquad_casd_df1_inst_q15* filterPtr = &m_filter; uint8_t i = 0; for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access @@ -97,7 +97,7 @@ void CFM::samples(q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - arm_biquad_cascade_df1_fast_q15(filterPtr, samples +i, ¤tSample, 1); + currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)))); currentSample += m_ctcssTX.getAudio(); diff --git a/FM.h b/FM.h index 474f36d..77a3d81 100644 --- a/FM.h +++ b/FM.h @@ -26,6 +26,7 @@ #include "FMTimeout.h" #include "FMKeyer.h" #include "FMTimer.h" +#include "FMDirectForm1.h" enum FM_STATE { FS_LISTENING, @@ -70,8 +71,9 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; - arm_biquad_casd_df1_inst_q15 m_filter; - q15_t m_filterState[12];//must be filterOrder * 4 long + CFMDirectFormI m_filterStage1; + CFMDirectFormI m_filterStage2; + CFMDirectFormI m_filterStage3; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/FMDirectForm1.h b/FMDirectForm1.h new file mode 100644 index 0000000..857e0a6 --- /dev/null +++ b/FMDirectForm1.h @@ -0,0 +1,114 @@ + +/******************************************************************************* +This header file has been taken from: +"A Collection of Useful C++ Classes for Digital Signal Processing" +By Vinnie Falco +Bernd Porr adapted it for Linux and turned it into a filter using +fixed point arithmetic. +-------------------------------------------------------------------------------- +License: MIT License (http://www.opensource.org/licenses/mit-license.php) +Copyright (c) 2009 by Vinnie Falco +Copyright (C) 2013-2017, Bernd Porr, mail@berndporr.me.uk +Copyright (C) 2020 , Mario Molitor , mario_molitor@web.de +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*******************************************************************************/ + +// based on https://raw.githubusercontent.com/berndporr/iir_fixed_point/master/DirectFormI.h + +#include "Globals.h" + +#ifndef DIRECTFORMI_HPP_ +#define DIRECTFORMI_HPP_ +class CFMDirectFormI +{ +public: + + // convenience function which takes the a0 argument but ignores it! + CFMDirectFormI(const q31_t b0, const q31_t b1, const q31_t b2, + const q31_t, const q31_t a1, const q31_t a2) + { + // FIR coefficients + c_b0 = b0; + c_b1 = b1; + c_b2 = b2; + // IIR coefficients + c_a1 = a1; + c_a2 = a2; + reset(); + } + + CFMDirectFormI(const CFMDirectFormI &my) + { + // delay line + m_x2 = my.m_x2; // x[n-2] + m_y2 = my.m_y2; // y[n-2] + m_x1 = my.m_x1; // x[n-1] + m_y1 = my.m_y1; // y[n-1] + + // coefficients + c_b0 = my.c_b0; + c_b1 = my.c_b1; + c_b2 = my.c_b2; // FIR + c_a1 = my.c_a1; + c_a2 = my.c_a2; // IIR + + // scaling factor + q_scaling = my.q_scaling; // 2^q_scaling + } + + void reset () + { + m_x1 = 0; + m_x2 = 0; + m_y1 = 0; + m_y2 = 0; + } + + // filtering operation: one sample in and one out + inline q15_t filter(const q15_t in) + { + // calculate the output + register q31_t out_upscaled = c_b0 * in //F4FXL puting stauration here made everything quiet, not sure why + + c_b1 * m_x1 + + c_b2 * m_x2 + - c_a1 * m_y1 + - c_a2 * m_y2; + + q15_t out = __SSAT(out_upscaled >> 15, 15); + + // update the delay lines + m_x2 = m_x1; + m_y2 = m_y1; + m_x1 = in; + m_y1 = out; + + return out; + } + +private: + // delay line + q31_t m_x2; // x[n-2] + q31_t m_y2; // y[n-2] + q31_t m_x1; // x[n-1] + q31_t m_y1; // y[n-1] + + // coefficients + q31_t c_b0,c_b1,c_b2; // FIR + q31_t c_a1,c_a2; // IIR +}; + +#endif /* DIRECTFORMI_HPP_ */ \ No newline at end of file diff --git a/FMGenerateFilterCoefficients.py b/FMGenerateFilterCoefficients.py new file mode 100644 index 0000000..84ea62b --- /dev/null +++ b/FMGenerateFilterCoefficients.py @@ -0,0 +1,45 @@ +# based on https://github.com/berndporr/iir_fixed_point/blob/master/gen_coeff.py + +import numpy as np +import scipy.signal as signal +import pylab as pl + +# Calculate the coefficients for a pure fixed point +# integer filter + +# sampling rate +fs = 24000 + +# cutoffs +f1 = 300 +f2 = 2700 +# ripple +rp = 0.2 + +# scaling factor in bits, do not change ! +q = 15 +# scaling factor as facor... +scaling_factor = 2**q + +# let's generate a sequence of 2nd order IIR filters +#sos = signal.butter(2,[f1/fs*2,f2/fs*2],'pass',output='sos') +sos = signal.cheby1(3,rp,[f1/fs*2,f2/fs*2],'bandpass', output='sos') + +sos = np.round((sos) * scaling_factor) + +# print coefficients +for biquad in sos: + for coeff in biquad: + print(int(coeff),",",sep="",end="") + #print((coeff),",",sep="",end="") + print("") + +# plot the frequency response +b,a = signal.sos2tf(sos) +w,h = signal.freqz(b,a) +pl.plot(w/np.pi/2*fs,20*np.log(np.abs(h))) +pl.xlabel('frequency/Hz'); +pl.ylabel('gain/dB'); +pl.ylim(top=1,bottom=-20); +pl.xlim(left=250, right=12000); +pl.show() \ No newline at end of file From 2057ef5206d43d80d3317b793cb0196390c45678 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Fri, 24 Apr 2020 09:20:26 +0200 Subject: [PATCH 076/119] clean up --- FM.cpp | 8 -------- FMDirectForm1.h | 9 +++------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/FM.cpp b/FM.cpp index 03aa6ec..019e27e 100644 --- a/FM.cpp +++ b/FM.cpp @@ -20,14 +20,6 @@ #include "Globals.h" #include "FM.h" -// 3 stage IIR Butterworth filter generated (if you change the order change the size of m_filterState). Also change the ordre in init call below -// 0.2db band pass ripple -// 300 - 2700Hz -q15_t FILTER_COEFFS[] = {362,724,362,16384,-18947,10676, - 16384,0,-16384,16384,-25170,9526, - 16384,-32768,16384,16384,-32037,15730}; - - CFM::CFM() : m_callsign(), m_rfAck(), diff --git a/FMDirectForm1.h b/FMDirectForm1.h index 857e0a6..4b90245 100644 --- a/FMDirectForm1.h +++ b/FMDirectForm1.h @@ -31,8 +31,8 @@ THE SOFTWARE. #include "Globals.h" -#ifndef DIRECTFORMI_HPP_ -#define DIRECTFORMI_HPP_ +#ifndef DIRECTFORMI_H_ +#define DIRECTFORMI_H_ class CFMDirectFormI { public: @@ -65,9 +65,6 @@ public: c_b2 = my.c_b2; // FIR c_a1 = my.c_a1; c_a2 = my.c_a2; // IIR - - // scaling factor - q_scaling = my.q_scaling; // 2^q_scaling } void reset () @@ -111,4 +108,4 @@ private: q31_t c_a1,c_a2; // IIR }; -#endif /* DIRECTFORMI_HPP_ */ \ No newline at end of file +#endif /* DIRECTFORMI_H */ \ No newline at end of file From 103d937152ea3c749b86e147f96ca02c3bc406e6 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 11:52:47 +0100 Subject: [PATCH 077/119] Remove unneeded line. --- FMCTCSSRX.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 6240f34..5c36f66 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -111,8 +111,6 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); - q31_t samp = q31_t(sample); - q31_t q2 = m_q1; m_q1 = m_q0; From 33f88344c30a650f42caa81c3840645e34886780 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 12:15:58 +0100 Subject: [PATCH 078/119] Run the holdoff timer permamnently. --- FM.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 019e27e..de6338c 100644 --- a/FM.cpp +++ b/FM.cpp @@ -114,14 +114,15 @@ uint8_t CFM::setCallsign(const char* callsign, uint8_t speed, uint16_t frequency m_callsignAtStart = callsignAtStart; m_callsignAtEnd = callsignAtEnd; - uint16_t holdoffTime = 0U; + uint16_t holdoffTime = holdoff * 60U; uint16_t callsignTime = time * 60U; - if (holdoff > 0U) - holdoffTime = callsignTime / holdoff; m_holdoffTimer.setTimeout(holdoffTime, 0U); m_callsignTimer.setTimeout(callsignTime, 0U); + if (holdoffTime > 0U) + m_holdoffTimer.start(); + return m_callsign.setParams(callsign, speed, frequency, highLevel, lowLevel); } @@ -190,7 +191,6 @@ void CFM::stateMachine(bool validSignal, uint8_t length) m_modemState = STATE_IDLE; m_callsignTimer.stop(); m_timeoutTimer.stop(); - m_holdoffTimer.stop(); m_kerchunkTimer.stop(); m_ackMinTimer.stop(); m_ackDelayTimer.stop(); @@ -237,7 +237,6 @@ void CFM::kerchunkState(bool validSignal) m_timeoutTimer.stop(); m_ackMinTimer.stop(); m_callsignTimer.stop(); - m_holdoffTimer.stop(); } } @@ -316,7 +315,6 @@ void CFM::hangState(bool validSignal) sendCallsign(); m_callsignTimer.stop(); - m_holdoffTimer.stop(); } } From e5c25f55fe4bb68b4f3065fe951bfceaf0e415f6 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 12:51:10 +0100 Subject: [PATCH 079/119] Remove the FM RX level. --- IO.cpp | 28 +++++----------------------- IO.h | 3 +-- SerialPort.cpp | 5 ++--- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/IO.cpp b/IO.cpp index a26b6d1..b0622ba 100644 --- a/IO.cpp +++ b/IO.cpp @@ -80,7 +80,6 @@ m_ysfTXLevel(128 * 128), m_p25TXLevel(128 * 128), m_nxdnTXLevel(128 * 128), m_pocsagTXLevel(128 * 128), -m_fmRXLevel(128 * 128), m_fmTXLevel(128 * 128), m_rxDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET), @@ -358,19 +357,11 @@ void CIO::process() } if (m_fmEnable) { - q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) - for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { - q31_t res1 = dcSamples[i] * m_fmRXLevel; - FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); - } + fm.samples(dcSamples, RX_BLOCK_SIZE); #else - for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { - q31_t res1 = samples[i] * m_fmRXLevel; - FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); - } + fm.samples(samples, RX_BLOCK_SIZE); #endif - fm.samples(FMVals, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -431,19 +422,11 @@ void CIO::process() nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_FM) { - q15_t FMVals[RX_BLOCK_SIZE]; #if defined(USE_DCBLOCKER) - for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { - q31_t res1 = dcSamples[i] * m_fmRXLevel; - FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); - } + fm.samples(dcSamples, RX_BLOCK_SIZE); #else - for (uint16_t i = 0U; i < RX_BLOCK_SIZE; i++) { - q31_t res1 = samples[i] * m_fmRXLevel; - FMVals[i] = q15_t(__SSAT((res1 >> 15), 16)); - } + fm.samples(samples, RX_BLOCK_SIZE); #endif - fm.samples(FMVals, RX_BLOCK_SIZE); } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); @@ -544,7 +527,7 @@ void CIO::setMode() #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, uint8_t fmRXLevel, uint8_t fmTXLevel, 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; @@ -556,7 +539,6 @@ 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_fmRXLevel = q15_t(fmRXLevel * 128); m_fmTXLevel = q15_t(fmTXLevel * 128); m_rxDCOffset = DC_OFFSET + rxDCOffset; diff --git a/IO.h b/IO.h index 68ada02..5ad4b1d 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, uint8_t fmRXLevel, uint8_t fmTXLevel, 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,7 +86,6 @@ private: q15_t m_p25TXLevel; q15_t m_nxdnTXLevel; q15_t m_pocsagTXLevel; - q15_t m_fmRXLevel; q15_t m_fmTXLevel; uint16_t m_rxDCOffset; diff --git a/SerialPort.cpp b/SerialPort.cpp index 121ee68..6818ddc 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -262,7 +262,7 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 20U) + if (length < 19U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; @@ -328,7 +328,6 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t pocsagTXLevel = data[17U]; uint8_t fmTXLevel = data[18U]; - uint8_t fmRXLevel = data[19U]; m_modemState = modemState; @@ -356,7 +355,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) ysfTX.setParams(ysfLoDev, ysfTXHang); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, fmRXLevel, txDCOffset, rxDCOffset); + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel, txDCOffset, rxDCOffset); io.start(); From 242bc7b32201e8d3032e9f392b11ae5448647f76 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 12:54:50 +0100 Subject: [PATCH 080/119] Bump the version date. --- SerialPort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPort.cpp b/SerialPort.cpp index 6818ddc..c0f44c7 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200421 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200424 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From 9fab1e08230cfddc4f57b8379c296035b2ff14c6 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 13:13:25 +0100 Subject: [PATCH 081/119] Rescale some of the tone levels. --- FM.h | 8 +++----- FMCTCSSTX.cpp | 2 +- FMKeyer.cpp | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/FM.h b/FM.h index 77a3d81..9bf4997 100644 --- a/FM.h +++ b/FM.h @@ -71,9 +71,9 @@ private: CFMTimer m_ackMinTimer; CFMTimer m_ackDelayTimer; CFMTimer m_hangTimer; - CFMDirectFormI m_filterStage1; - CFMDirectFormI m_filterStage2; - CFMDirectFormI m_filterStage3; + CFMDirectFormI m_filterStage1; + CFMDirectFormI m_filterStage2; + CFMDirectFormI m_filterStage3; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); @@ -86,8 +86,6 @@ private: void sendCallsign(); void beginRelaying(); - - q15_t filter(q15_t sample); }; #endif diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index fb5a997..ce11d9f 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -106,7 +106,7 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) q15_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q31_t value = ::arm_sin_q15(arg) * q15_t(level * 128); + q31_t value = ::arm_sin_q15(arg) * q15_t(level * 1280); m_values[i] = q15_t(__SSAT((value >> 15), 16)); arg += entry->increment; diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 70beea4..2b49dac 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -111,8 +111,8 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, } } - m_highLevel = q15_t(highLevel * 128); - m_lowLevel = q15_t(lowLevel * 128); + m_highLevel = q15_t(highLevel); + m_lowLevel = q15_t(lowLevel); m_dotLen = 24000U / speed; // In samples From 868d33ebd2c90da25d7a874f2ec9d0025d218712 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Fri, 24 Apr 2020 15:00:40 +0100 Subject: [PATCH 082/119] Rescale the CTCSS TX level. --- FMCTCSSTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index ce11d9f..4318c67 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -106,7 +106,7 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) q15_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q31_t value = ::arm_sin_q15(arg) * q15_t(level * 1280); + q31_t value = ::arm_sin_q15(arg) * q15_t(level * 13); m_values[i] = q15_t(__SSAT((value >> 15), 16)); arg += entry->increment; From 0b27ec5443b93486802b649d6f0f57e289952c23 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 25 Apr 2020 08:04:29 +0200 Subject: [PATCH 083/119] Fix STM32F10X compile complaining about duplicate stuff --- FMCTCSSRX.cpp | 8 ++++---- FMCTCSSTX.cpp | 10 +++++----- FMKeyer.cpp | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 5c36f66..6251f63 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -20,10 +20,10 @@ #include "Globals.h" #include "FMCTCSSRX.h" -const struct CTCSS_TABLE { +const struct RX_CTCSS_TABLE { uint8_t frequency; q63_t coeffDivTwo; -} CTCSS_TABLE_DATA[] = { +} RX_CTCSS_TABLE_DATA[] = { { 67U, 2147153298}, { 69U, 2147130228}, { 71U, 2147103212}, @@ -93,8 +93,8 @@ m_result(CTS_NONE) uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { - if (CTCSS_TABLE_DATA[i].frequency == frequency) { - m_coeffDivTwo = CTCSS_TABLE_DATA[i].coeffDivTwo; + if (RX_CTCSS_TABLE_DATA[i].frequency == frequency) { + m_coeffDivTwo = RX_CTCSS_TABLE_DATA[i].coeffDivTwo; break; } } diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 4318c67..6eee38d 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -20,11 +20,11 @@ #include "Globals.h" #include "FMCTCSSTX.h" -const struct CTCSS_TABLE { +const struct TX_CTCSS_TABLE { uint8_t frequency; uint16_t length; q15_t increment; -} CTCSS_TABLE_DATA[] = { +} TX_CTCSS_TABLE_DATA[] = { { 67U, 358U, 92}, { 69U, 346U, 95}, { 71U, 334U, 99}, @@ -88,11 +88,11 @@ m_n(0U) uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) { - const CTCSS_TABLE* entry = NULL; + const TX_CTCSS_TABLE* entry = NULL; for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { - if (CTCSS_TABLE_DATA[i].frequency == frequency) { - entry = CTCSS_TABLE_DATA + i; + if (TX_CTCSS_TABLE_DATA[i].frequency == frequency) { + entry = TX_CTCSS_TABLE_DATA + i; break; } } diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 2b49dac..55eeb8b 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -72,8 +72,8 @@ const struct { const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; -#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) -#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) +#define WRITE_BIT_FM(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT_FM(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) CFMKeyer::CFMKeyer() : m_wanted(false), @@ -98,7 +98,7 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint32_t MASK = 0x80000000U; for (uint8_t k = 0U; k < SYMBOL_LIST[j].length; k++, m_poLen++, MASK >>= 1) { bool b = (SYMBOL_LIST[j].pattern & MASK) == MASK; - WRITE_BIT(m_poBuffer, m_poLen, b); + WRITE_BIT_FM(m_poBuffer, m_poLen, b); if (m_poLen >= 995U) { m_poLen = 0U; @@ -136,7 +136,7 @@ q15_t CFMKeyer::getHighAudio() if (!m_wanted) return 0U; - bool b = READ_BIT(m_poBuffer, m_poPos); + bool b = READ_BIT_FM(m_poBuffer, m_poPos); if (b) output = m_audio[m_audioPos] ? m_highLevel : -m_highLevel; @@ -162,7 +162,7 @@ q15_t CFMKeyer::getLowAudio() if (!m_wanted) return 0U; - bool b = READ_BIT(m_poBuffer, m_poPos); + bool b = READ_BIT_FM(m_poBuffer, m_poPos); if (b) output = m_audio[m_audioPos] ? m_lowLevel : -m_lowLevel; From 6ac6deba10fedc77d97f28016a4017c778a65c54 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 25 Apr 2020 15:17:11 +0100 Subject: [PATCH 084/119] Add two more FM parameters. --- FM.cpp | 22 ++++++++++++++++------ FM.h | 6 ++++-- IO.cpp | 10 ++++++---- SerialPort.cpp | 10 +++++++--- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/FM.cpp b/FM.cpp index de6338c..7f0beb0 100644 --- a/FM.cpp +++ b/FM.cpp @@ -38,14 +38,19 @@ m_ackDelayTimer(), m_hangTimer(), m_filterStage1( 724, 1448, 724, 32768, -37895, 21352), m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), -m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460) +m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), +m_useCOS(true), +m_rxBoost(1U) { } -void CFM::samples(q15_t* samples, uint8_t length) +void CFM::samples(bool cos, q15_t* samples, uint8_t length) { uint8_t i = 0; for (; i < length; i++) { + if (!m_useCOS) + cos = true; + q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access CTCSSState ctcssState = m_ctcssRX.process(currentSample); @@ -56,26 +61,28 @@ void CFM::samples(q15_t* samples, uint8_t length) } else if (CTCSS_READY(ctcssState) && m_modemState != STATE_FM) { //we had enough samples for CTCSS and we are in some other mode than FM bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS, i + 1U); + stateMachine(validCTCSS && cos, i + 1U); if (m_modemState != STATE_FM) continue; } else if (CTCSS_READY(ctcssState) && m_modemState == STATE_FM) { //We had enough samples for CTCSS and we are in FM mode, trigger the state machine bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS, i + 1U); + stateMachine(validCTCSS && cos, i + 1U); if (m_modemState != STATE_FM) break; } else if (CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { //Not enough samples for CTCSS but we already are in FM, trigger the state machine //but do not trigger the state machine on every single sample, save CPU! bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS, i + 1U); + stateMachine(validCTCSS && cos, i + 1U); } // Only let audio through when relaying audio if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) currentSample = 0U; + currentSample *= m_rxBoost; + if (!m_callsign.isRunning()) currentSample += m_rfAck.getHighAudio(); @@ -134,8 +141,11 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ return m_rfAck.setParams(rfAck, speed, frequency, level, level); } -uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost) { + m_useCOS = useCOS; + m_rxBoost = q15_t(rxBoost); + m_timeoutTimer.setTimeout(timeout, 0U); m_kerchunkTimer.setTimeout(kerchunkTime, 0U); m_hangTimer.setTimeout(hangTime, 0U); diff --git a/FM.h b/FM.h index 9bf4997..e451ed6 100644 --- a/FM.h +++ b/FM.h @@ -45,7 +45,7 @@ class CFM { public: CFM(); - void samples(q15_t* samples, uint8_t length); + void samples(bool cos, q15_t* samples, uint8_t length); void process(); @@ -53,7 +53,7 @@ public: uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost); private: CFMKeyer m_callsign; @@ -74,6 +74,8 @@ private: CFMDirectFormI m_filterStage1; CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; + bool m_useCOS; + q15_t m_rxBoost; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/IO.cpp b/IO.cpp index b0622ba..a737693 100644 --- a/IO.cpp +++ b/IO.cpp @@ -357,10 +357,11 @@ void CIO::process() } if (m_fmEnable) { + bool cos = getCOSInt(); #if defined(USE_DCBLOCKER) - fm.samples(dcSamples, RX_BLOCK_SIZE); + fm.samples(cos, dcSamples, RX_BLOCK_SIZE); #else - fm.samples(samples, RX_BLOCK_SIZE); + fm.samples(cos, samples, RX_BLOCK_SIZE); #endif } } else if (m_modemState == STATE_DSTAR) { @@ -422,10 +423,11 @@ 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(dcSamples, RX_BLOCK_SIZE); + fm.samples(cos, dcSamples, RX_BLOCK_SIZE); #else - fm.samples(samples, RX_BLOCK_SIZE); + fm.samples(cos, samples, RX_BLOCK_SIZE); #endif } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; diff --git a/SerialPort.cpp b/SerialPort.cpp index c0f44c7..aa71f32 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200424 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200425 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -408,7 +408,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 7U) + if (length < 9U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -421,7 +421,11 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t kerchunkTime = data[5U]; uint8_t hangTime = data[6U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime); + bool useCOS = (data[7U] & 0x01U) == 0x01U; + + uint8_t rxBoost = data[8U]; + + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From 5295e0bdc357995150bb0b9653f48f3bdd6d915e Mon Sep 17 00:00:00 2001 From: m0vse Date: Sat, 25 Apr 2020 16:13:13 +0100 Subject: [PATCH 085/119] Fix for Ack/Callsign corruption --- FMCTCSSTX.cpp | 3 +++ FMKeyer.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 6eee38d..7b45c1e 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -102,6 +102,9 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) m_length = entry->length; + if (m_values) + delete[] m_values; + m_values = new q15_t[m_length]; q15_t arg = 0; diff --git a/FMKeyer.cpp b/FMKeyer.cpp index 55eeb8b..a779d0e 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -92,6 +92,7 @@ m_lowLevel(0) uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel) { + m_poLen=0; for (uint8_t i = 0U; text[i] != '\0'; i++) { for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { if (SYMBOL_LIST[j].c == text[i]) { @@ -118,6 +119,9 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, m_audioLen = 24000U / frequency; // In samples + if (m_audio) + delete[] m_audio; + m_audio = new bool[m_audioLen]; for (uint16_t i = 0U; i < m_audioLen; i++) { From 4fe1c11250bf8908571ecaabe6c8bf6b599d582f Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 25 Apr 2020 17:29:10 +0100 Subject: [PATCH 086/119] Remove memory leak and other configuration funnies. --- FM.cpp | 8 ++++---- FMCTCSSTX.cpp | 4 +--- FMKeyer.cpp | 7 +++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/FM.cpp b/FM.cpp index 7f0beb0..d3e9a1a 100644 --- a/FM.cpp +++ b/FM.cpp @@ -46,11 +46,11 @@ m_rxBoost(1U) void CFM::samples(bool cos, q15_t* samples, uint8_t length) { - uint8_t i = 0; - for (; i < length; i++) { - if (!m_useCOS) - cos = true; + if (!m_useCOS) + cos = true; + uint8_t i = 0U; + for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access CTCSSState ctcssState = m_ctcssRX.process(currentSample); diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 7b45c1e..e513d70 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -102,9 +102,7 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) m_length = entry->length; - if (m_values) - delete[] m_values; - + delete[] m_values; m_values = new q15_t[m_length]; q15_t arg = 0; diff --git a/FMKeyer.cpp b/FMKeyer.cpp index a779d0e..36a2815 100644 --- a/FMKeyer.cpp +++ b/FMKeyer.cpp @@ -92,7 +92,8 @@ m_lowLevel(0) uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, uint8_t highLevel, uint8_t lowLevel) { - m_poLen=0; + m_poLen = 0U; + for (uint8_t i = 0U; text[i] != '\0'; i++) { for (uint8_t j = 0U; SYMBOL_LIST[j].c != 0U; j++) { if (SYMBOL_LIST[j].c == text[i]) { @@ -119,9 +120,7 @@ uint8_t CFMKeyer::setParams(const char* text, uint8_t speed, uint16_t frequency, m_audioLen = 24000U / frequency; // In samples - if (m_audio) - delete[] m_audio; - + delete[] m_audio; m_audio = new bool[m_audioLen]; for (uint16_t i = 0U; i < m_audioLen; i++) { From 8c38fdb36d22af2919791d83ddbd3e82381d0df2 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 25 Apr 2020 17:37:37 +0100 Subject: [PATCH 087/119] Clean out the CTCSS decoder coefficient before initialising. --- FMCTCSSRX.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 6251f63..164af75 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -92,6 +92,8 @@ m_result(CTS_NONE) uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) { + m_coeffDivTwo = 0; + for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { if (RX_CTCSS_TABLE_DATA[i].frequency == frequency) { m_coeffDivTwo = RX_CTCSS_TABLE_DATA[i].coeffDivTwo; From d2d50e21e1125771e28a8a3d8fc338e79ca1ae57 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 25 Apr 2020 22:12:16 +0200 Subject: [PATCH 088/119] CTCSS threshold is level agnostic, experimental --- FMCTCSSRX.cpp | 16 ++++++++++++++-- IO.cpp | 5 +++++ IO.h | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 164af75..5e3ecd4 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,10 +109,21 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) return 0U; } +char bla[256]; CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); + // sample / rxLevel + q15_t rxLevel = io.getRxLevel(); + q31_t sample31 = q31_t(sample) << 16; + if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) + sample31 += rxLevel >> 1; + else + sample31 -= rxLevel >> 1; + sample31 /= rxLevel; + + q31_t q2 = m_q1; m_q1 = m_q0; @@ -122,7 +133,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + q31_t(sample); + m_q0 = t3 - q2 + sample31; m_count++; if (m_count == N) { @@ -143,7 +154,8 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 q31_t value = t2 + t4 - t9; - + sprintf(bla, "CTCSS Value / Threshold %d %d", value, m_threshold); + DEBUG1(bla); m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; diff --git a/IO.cpp b/IO.cpp index a737693..afd56b0 100644 --- a/IO.cpp +++ b/IO.cpp @@ -592,3 +592,8 @@ bool CIO::hasLockout() const { return m_lockout; } + +q15_t CIO::getRxLevel() const +{ + return m_rxLevel; +} diff --git a/IO.h b/IO.h index 5ad4b1d..f3ac4f9 100644 --- a/IO.h +++ b/IO.h @@ -56,6 +56,8 @@ public: void selfTest(); + q15_t getRxLevel() const; + private: bool m_started; From 23cf22d87da54e2a3a01aa74cfc3ac62c25b3575 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 25 Apr 2020 23:07:41 +0200 Subject: [PATCH 089/119] remove Debug info --- FMCTCSSRX.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 5e3ecd4..37e2772 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,7 +109,6 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) return 0U; } -char bla[256]; CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); @@ -122,7 +121,6 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) else sample31 -= rxLevel >> 1; sample31 /= rxLevel; - q31_t q2 = m_q1; m_q1 = m_q0; @@ -154,8 +152,6 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 q31_t value = t2 + t4 - t9; - sprintf(bla, "CTCSS Value / Threshold %d %d", value, m_threshold); - DEBUG1(bla); m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; From b963b2b848638fdee4e1fe9808d7ddd66e022315 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 25 Apr 2020 22:32:10 +0100 Subject: [PATCH 090/119] Add optional over deviation blanking. --- FM.cpp | 8 +++-- FM.h | 4 ++- FMBlanking.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ FMBlanking.h | 41 +++++++++++++++++++++++++ SerialPort.cpp | 5 +-- 5 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 FMBlanking.cpp create mode 100644 FMBlanking.h diff --git a/FM.cpp b/FM.cpp index d3e9a1a..33938d8 100644 --- a/FM.cpp +++ b/FM.cpp @@ -39,6 +39,7 @@ m_hangTimer(), m_filterStage1( 724, 1448, 724, 32768, -37895, 21352), m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), +m_blanking(), m_useCOS(true), m_rxBoost(1U) { @@ -78,7 +79,9 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) } // Only let audio through when relaying audio - if (m_state != FS_RELAYING && m_state != FS_KERCHUNK) + if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) + currentSample = m_blanking.process(currentSample); + else currentSample = 0U; currentSample *= m_rxBoost; @@ -141,7 +144,7 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ return m_rfAck.setParams(rfAck, speed, frequency, level, level); } -uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev) { m_useCOS = useCOS; m_rxBoost = q15_t(rxBoost); @@ -151,6 +154,7 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_hangTimer.setTimeout(hangTime, 0U); m_timeoutTone.setParams(timeoutLevel); + m_blanking.setParams(maxDev, timeoutLevel); uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold); if (ret != 0U) diff --git a/FM.h b/FM.h index e451ed6..ad75877 100644 --- a/FM.h +++ b/FM.h @@ -21,6 +21,7 @@ #include "Config.h" +#include "FMBlanking.h" #include "FMCTCSSRX.h" #include "FMCTCSSTX.h" #include "FMTimeout.h" @@ -53,7 +54,7 @@ public: uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev); private: CFMKeyer m_callsign; @@ -74,6 +75,7 @@ private: CFMDirectFormI m_filterStage1; CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; + CFMBlanking m_blanking; bool m_useCOS; q15_t m_rxBoost; diff --git a/FMBlanking.cpp b/FMBlanking.cpp new file mode 100644 index 0000000..4d8af80 --- /dev/null +++ b/FMBlanking.cpp @@ -0,0 +1,82 @@ +/* + * 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 "FMBlanking.h" + +// 2000 Hz sine wave at 24000 Hz sample rate +const q31_t TONE[] = {0, 16384, 28378, 32767, 28378, 16384, 0, -16383, -28377, -32767, -28377, -16383}; + +const uint8_t TONE_LEN = 12U; + +const uint32_t BLEEP_LEN = 2400U; // 100ms + +CFMBlanking::CFMBlanking() : +m_posValue(0), +m_negValue(0), +m_level(128 * 128), +m_running(false), +m_pos(0U), +m_n(0U) +{ +} + +void CFMBlanking::setParams(uint8_t value, uint8_t level) +{ + m_posValue = q15_t(value * 128); + m_negValue = -m_posValue; + + m_level = q15_t(level * 128); +} + +q15_t CFMBlanking::process(q15_t sample) +{ + if (m_posValue == 0) + return sample; + + if (!m_running) { + if (sample >= m_posValue) { + m_running = true; + m_pos = 0U; + m_n = 0U; + } else if (sample <= m_negValue) { + m_running = true; + m_pos = 0U; + m_n = 0U; + } + } + + if (!m_running) + return sample; + + if (m_pos <= BLEEP_LEN) { + q31_t value = TONE[m_n++] * m_level; + sample = q15_t(__SSAT((value >> 15), 16)); + if (m_n >= TONE_LEN) + m_n = 0U; + } else { + sample = 0; + } + + m_pos++; + if (m_pos >= 12000U) + m_running = false; + + return sample; +} diff --git a/FMBlanking.h b/FMBlanking.h new file mode 100644 index 0000000..2aacd4c --- /dev/null +++ b/FMBlanking.h @@ -0,0 +1,41 @@ +/* + * 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(FMBlanking_H) +#define FMBlanking_H + +#include "Config.h" + +class CFMBlanking { +public: + CFMBlanking(); + + void setParams(uint8_t value, uint8_t level); + + q15_t process(q15_t sample); + +private: + q15_t m_posValue; + q15_t m_negValue; + q15_t m_level; + bool m_running; + uint32_t m_pos; + uint8_t m_n; +}; + +#endif diff --git a/SerialPort.cpp b/SerialPort.cpp index aa71f32..4b0b5bc 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -408,7 +408,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 9U) + if (length < 10U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -424,8 +424,9 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) bool useCOS = (data[7U] & 0x01U) == 0x01U; uint8_t rxBoost = data[8U]; + uint8_t maxDev = data[9U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost); + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost, maxDev); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From bd4096b6be27cdb1b1289cf2d080b8fd6fb83cd1 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sat, 25 Apr 2020 23:48:02 +0100 Subject: [PATCH 091/119] Revert "Make CTCSS threshold RXLevel and RXBoost agnostic" --- FMCTCSSRX.cpp | 12 ++---------- IO.cpp | 5 ----- IO.h | 2 -- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 37e2772..164af75 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -113,15 +113,6 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); - // sample / rxLevel - q15_t rxLevel = io.getRxLevel(); - q31_t sample31 = q31_t(sample) << 16; - if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) - sample31 += rxLevel >> 1; - else - sample31 -= rxLevel >> 1; - sample31 /= rxLevel; - q31_t q2 = m_q1; m_q1 = m_q0; @@ -131,7 +122,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + sample31; + m_q0 = t3 - q2 + q31_t(sample); m_count++; if (m_count == N) { @@ -152,6 +143,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 q31_t value = t2 + t4 - t9; + m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; diff --git a/IO.cpp b/IO.cpp index afd56b0..a737693 100644 --- a/IO.cpp +++ b/IO.cpp @@ -592,8 +592,3 @@ bool CIO::hasLockout() const { return m_lockout; } - -q15_t CIO::getRxLevel() const -{ - return m_rxLevel; -} diff --git a/IO.h b/IO.h index f3ac4f9..5ad4b1d 100644 --- a/IO.h +++ b/IO.h @@ -56,8 +56,6 @@ public: void selfTest(); - q15_t getRxLevel() const; - private: bool m_started; From d973238596176a10cb4e970f6f86968443a18063 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sat, 25 Apr 2020 22:12:16 +0200 Subject: [PATCH 092/119] CTCSS threshold is level agnostic, experimental (cherry picked from commit d2d50e21e1125771e28a8a3d8fc338e79ca1ae57) --- FMCTCSSRX.cpp | 16 ++++++++++++++-- IO.cpp | 5 +++++ IO.h | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 164af75..5e3ecd4 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,10 +109,21 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) return 0U; } +char bla[256]; CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); + // sample / rxLevel + q15_t rxLevel = io.getRxLevel(); + q31_t sample31 = q31_t(sample) << 16; + if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) + sample31 += rxLevel >> 1; + else + sample31 -= rxLevel >> 1; + sample31 /= rxLevel; + + q31_t q2 = m_q1; m_q1 = m_q0; @@ -122,7 +133,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + q31_t(sample); + m_q0 = t3 - q2 + sample31; m_count++; if (m_count == N) { @@ -143,7 +154,8 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 q31_t value = t2 + t4 - t9; - + sprintf(bla, "CTCSS Value / Threshold %d %d", value, m_threshold); + DEBUG1(bla); m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; diff --git a/IO.cpp b/IO.cpp index a737693..afd56b0 100644 --- a/IO.cpp +++ b/IO.cpp @@ -592,3 +592,8 @@ bool CIO::hasLockout() const { return m_lockout; } + +q15_t CIO::getRxLevel() const +{ + return m_rxLevel; +} diff --git a/IO.h b/IO.h index 5ad4b1d..f3ac4f9 100644 --- a/IO.h +++ b/IO.h @@ -56,6 +56,8 @@ public: void selfTest(); + q15_t getRxLevel() const; + private: bool m_started; From 103116e410a6dd5098a1bd25b5c54d4d7772aeb9 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 26 Apr 2020 07:26:35 +0200 Subject: [PATCH 093/119] Add debug essage when CTCSS status changes --- FMCTCSSRX.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 5e3ecd4..7baadab 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -109,7 +109,6 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) return 0U; } -char bla[256]; CTCSSState CFMCTCSSRX::process(q15_t sample) { m_result = m_result & (~CTS_READY); @@ -154,14 +153,17 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) // value = m_q0 * m_q0 + m_q1 * m_q1 - m_q0 * m_q1 * m_coeffDivTwo * 2 q31_t value = t2 + t4 - t9; - sprintf(bla, "CTCSS Value / Threshold %d %d", value, m_threshold); - DEBUG1(bla); + + bool previousCTCSSValid = CTCSS_VALID(m_result); m_result = m_result | CTS_READY; if (value >= m_threshold) m_result = m_result | CTS_VALID; else m_result = m_result & ~CTS_VALID; + if(previousCTCSSValid != CTCSS_VALID(m_result)) + DEBUG4("CTCSS Value / Threshold / Valid", value, m_threshold, CTCSS_VALID(m_result)); + m_count = 0U; m_q0 = 0; m_q1 = 0; From 5051a13711c2151f976a932ed660121686b4fc9b Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 26 Apr 2020 08:16:08 +0200 Subject: [PATCH 094/119] Fix invalid values passed to arm_sin_q15 --- FMCTCSSTX.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index e513d70..754612a 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -111,6 +111,8 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) m_values[i] = q15_t(__SSAT((value >> 15), 16)); arg += entry->increment; + if(arg < 0)//did we exceed max value of q15_t and flip over ? + arg += 32768;//bring arg back to valid values for ::arm_sin_q15, as per doc it needs to be in the range 0 to 0.99999999 (aka 0 - 32767) } return 0U; From 6824d5662accc3d680209e1bfcfcd92182101184 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 26 Apr 2020 12:24:05 +0200 Subject: [PATCH 095/119] Remove dependency to IO in CTCSSRX --- FM.cpp | 16 +++++++++++++++- FM.h | 2 ++ FMCTCSSRX.cpp | 14 ++------------ FMCTCSSRX.h | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/FM.cpp b/FM.cpp index 33938d8..ad8b5f5 100644 --- a/FM.cpp +++ b/FM.cpp @@ -54,7 +54,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access - CTCSSState ctcssState = m_ctcssRX.process(currentSample); + CTCSSState ctcssState = m_ctcssRX.process(getUnscaledSample(currentSample)); if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { //Not enough samples to determine if you have CTCSS, just carry on @@ -397,3 +397,17 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } + +q15_t CFM::getUnscaledSample(q15_t sample) +{ + // sample / rxLevel + q15_t rxLevel = io.getRxLevel(); + q31_t sample31 = q31_t(sample) << 16; + if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) + sample31 += rxLevel >> 1; + else + sample31 -= rxLevel >> 1; + sample31 /= rxLevel; + + return q15_t(sample31); +} diff --git a/FM.h b/FM.h index ad75877..f2190d7 100644 --- a/FM.h +++ b/FM.h @@ -90,6 +90,8 @@ private: void sendCallsign(); void beginRelaying(); + + q15_t getUnscaledSample(q15_t sample); }; #endif diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 7baadab..ade0491 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -111,17 +111,7 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) CTCSSState CFMCTCSSRX::process(q15_t sample) { - m_result = m_result & (~CTS_READY); - - // sample / rxLevel - q15_t rxLevel = io.getRxLevel(); - q31_t sample31 = q31_t(sample) << 16; - if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) - sample31 += rxLevel >> 1; - else - sample31 -= rxLevel >> 1; - sample31 /= rxLevel; - + m_result = m_result & (~CTS_READY); q31_t q2 = m_q1; m_q1 = m_q0; @@ -132,7 +122,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + sample31; + m_q0 = t3 - q2 + sample; m_count++; if (m_count == N) { diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 8d2adaa..a848cb9 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -55,7 +55,7 @@ public: uint8_t setParams(uint8_t frequency, uint8_t threshold); - CTCSSState process(q15_t samples); + CTCSSState process(q15_t sample); CTCSSState getState(); From 028d0cf609dcb78b1ad70ac030284de1f76f0669 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Sun, 26 Apr 2020 12:38:23 +0200 Subject: [PATCH 096/119] Add q31_t cast --- FMCTCSSRX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index ade0491..57fbfbe 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -122,7 +122,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + sample; + m_q0 = t3 - q2 + q31_t(sample); m_count++; if (m_count == N) { From a31cf3d6f8a99162432c0d73508218dc7a783e19 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 26 Apr 2020 12:07:10 +0100 Subject: [PATCH 097/119] Convert the CTCSS TX tones to Q31 from Q15. --- FMCTCSSTX.cpp | 109 ++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 56 deletions(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 754612a..37241f9 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -23,59 +23,58 @@ const struct TX_CTCSS_TABLE { uint8_t frequency; uint16_t length; - q15_t increment; + q31_t increment; } TX_CTCSS_TABLE_DATA[] = { - { 67U, 358U, 92}, - { 69U, 346U, 95}, - { 71U, 334U, 99}, - { 74U, 323U, 102}, - { 77U, 312U, 106}, - { 79U, 301U, 109}, - { 82U, 291U, 113}, - { 85U, 281U, 117}, - { 88U, 271U, 121}, - { 91U, 262U, 125}, - { 94U, 253U, 130}, - { 97U, 246U, 133}, - {100U, 240U, 137}, - {103U, 232U, 142}, - {107U, 224U, 147}, - {110U, 216U, 152}, - {114U, 209U, 157}, - {118U, 202U, 163}, - {123U, 195U, 168}, - {127U, 189U, 174}, - {131U, 182U, 180}, - {136U, 176U, 187}, - {141U, 170U, 193}, - {146U, 164U, 200}, - {151U, 159U, 207}, - {156U, 153U, 214}, - {159U, 150U, 219}, - {162U, 148U, 222}, - {165U, 145U, 226}, - {167U, 143U, 230}, - {171U, 140U, 234}, - {173U, 138U, 238}, - {177U, 135U, 243}, - {179U, 133U, 246}, - {183U, 131U, 251}, - {186U, 129U, 255}, - {189U, 126U, 260}, - {192U, 124U, 264}, - {196U, 122U, 269}, - {199U, 120U, 273}, - {203U, 118U, 278}, - {206U, 116U, 282}, - {210U, 114U, 288}, - {218U, 110U, 298}, - {225U, 106U, 309}, - {229U, 105U, 313}, - {233U, 103U, 319}, - {241U, 99U, 331}, - {250U, 96U, 342}, - {254U, 94U, 347} -}; + { 67U, 358U, 5995059}, + { 69U, 346U, 6200860}, + { 71U, 334U, 6433504}, + { 74U, 323U, 6657200}, + { 77U, 312U, 6889844}, + { 79U, 301U, 7131436}, + { 82U, 291U, 7381976}, + { 85U, 281U, 7641463}, + { 88U, 271U, 7918846}, + { 91U, 262U, 8187282}, + { 94U, 253U, 8482561}, + { 97U, 246U, 8715205}, + {100U, 240U, 8947849}, + {103U, 232U, 9261024}, + {107U, 224U, 9592094}, + {110U, 216U, 9923165}, + {114U, 209U, 10272131}, + {118U, 202U, 10630045}, + {123U, 195U, 11005854}, + {127U, 189U, 11390612}, + {131U, 182U, 11793265}, + {136U, 176U, 12213814}, + {141U, 170U, 12643310}, + {146U, 164U, 13081755}, + {151U, 159U, 13547043}, + {156U, 153U, 14021279}, + {159U, 150U, 14298662}, + {162U, 148U, 14513411}, + {165U, 145U, 14808690}, + {167U, 143U, 15023438}, + {171U, 140U, 15327665}, + {173U, 138U, 15551361}, + {177U, 135U, 15864536}, + {179U, 133U, 16097180}, + {183U, 131U, 16419303}, + {186U, 129U, 16660894}, + {189U, 126U, 16991965}, + {192U, 124U, 17251452}, + {196U, 122U, 17591471}, + {199U, 120U, 17850958}, + {203U, 118U, 18208872}, + {206U, 116U, 18477308}, + {210U, 114U, 18853117}, + {218U, 110U, 19515258}, + {225U, 106U, 20195295}, + {229U, 105U, 20499521}, + {233U, 103U, 20902175}, + {241U, 99U, 21635898}, + {250U, 96U, 22396465}, + {254U, 94U, 22736484}}; const uint8_t CTCSS_TABLE_DATA_LEN = 50U; @@ -105,14 +104,12 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) delete[] m_values; m_values = new q15_t[m_length]; - q15_t arg = 0; + q31_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q31_t value = ::arm_sin_q15(arg) * q15_t(level * 13); + q31_t value = ::arm_sin_q31(arg) * q15_t(level * 13); m_values[i] = q15_t(__SSAT((value >> 15), 16)); arg += entry->increment; - if(arg < 0)//did we exceed max value of q15_t and flip over ? - arg += 32768;//bring arg back to valid values for ::arm_sin_q15, as per doc it needs to be in the range 0 to 0.99999999 (aka 0 - 32767) } return 0U; From 6f4c19c006829e4013cb91d21ecab385953c7ad1 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 26 Apr 2020 13:02:43 +0100 Subject: [PATCH 098/119] Convert to Q47 arithmetic. --- FMCTCSSTX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 37241f9..8a34b5f 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -106,8 +106,8 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) q31_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q31_t value = ::arm_sin_q31(arg) * q15_t(level * 13); - m_values[i] = q15_t(__SSAT((value >> 15), 16)); + q63_t value = ::arm_sin_q31(arg) * q15_t(level * 13); + m_values[i] = q15_t(__SSAT((value >> 31), 16)); arg += entry->increment; } From 458a3d2d240c68d9fbfd304345c4de466d149329 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 26 Apr 2020 13:22:16 +0100 Subject: [PATCH 099/119] Bump the version date. --- SerialPort.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPort.cpp b/SerialPort.cpp index 4b0b5bc..f729cb3 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200425 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200426 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From d66c6d7f9c356caa0d527100bcfe194df88b9cd8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 26 Apr 2020 15:24:51 +0100 Subject: [PATCH 100/119] Fix the multiplication. --- FMCTCSSTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSTX.cpp b/FMCTCSSTX.cpp index 8a34b5f..fcffbb5 100644 --- a/FMCTCSSTX.cpp +++ b/FMCTCSSTX.cpp @@ -106,7 +106,7 @@ uint8_t CFMCTCSSTX::setParams(uint8_t frequency, uint8_t level) q31_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q63_t value = ::arm_sin_q31(arg) * q15_t(level * 13); + q63_t value = ::arm_sin_q31(arg) * q63_t(level * 13); m_values[i] = q15_t(__SSAT((value >> 31), 16)); arg += entry->increment; From f9530ee82acb13e047812f1f9e7a8263ab8af1ad Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Sun, 26 Apr 2020 22:01:11 +0100 Subject: [PATCH 101/119] Pass the RX level to the FM controller. --- FM.cpp | 17 ++++++++++------- FM.h | 3 ++- IO.cpp | 5 ----- IO.h | 4 +--- SerialPort.cpp | 5 +++-- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/FM.cpp b/FM.cpp index ad8b5f5..074de3d 100644 --- a/FM.cpp +++ b/FM.cpp @@ -41,7 +41,8 @@ m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), m_useCOS(true), -m_rxBoost(1U) +m_rxBoost(1U), +m_rxLevel(128 * 128) { } @@ -144,10 +145,11 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ return m_rfAck.setParams(rfAck, speed, frequency, level, level); } -uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev, uint8_t rxLevel) { m_useCOS = useCOS; m_rxBoost = q15_t(rxBoost); + m_rxLevel = q15_t(rxLevel * 128); m_timeoutTimer.setTimeout(timeout, 0U); m_kerchunkTimer.setTimeout(kerchunkTime, 0U); @@ -401,13 +403,14 @@ void CFM::beginRelaying() q15_t CFM::getUnscaledSample(q15_t sample) { // sample / rxLevel - q15_t rxLevel = io.getRxLevel(); q31_t sample31 = q31_t(sample) << 16; - if (((sample31 >> 31) & 1) == ((rxLevel >> 15) & 1)) - sample31 += rxLevel >> 1; + + if (((sample31 >> 31) & 1) == ((m_rxLevel >> 15) & 1)) + sample31 += m_rxLevel >> 1; else - sample31 -= rxLevel >> 1; - sample31 /= rxLevel; + sample31 -= m_rxLevel >> 1; + + sample31 /= m_rxLevel; return q15_t(sample31); } diff --git a/FM.h b/FM.h index f2190d7..1a07eb8 100644 --- a/FM.h +++ b/FM.h @@ -54,7 +54,7 @@ public: uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev, uint8_t rxLevel); private: CFMKeyer m_callsign; @@ -78,6 +78,7 @@ private: CFMBlanking m_blanking; bool m_useCOS; q15_t m_rxBoost; + q15_t m_rxLevel; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/IO.cpp b/IO.cpp index afd56b0..a737693 100644 --- a/IO.cpp +++ b/IO.cpp @@ -592,8 +592,3 @@ bool CIO::hasLockout() const { return m_lockout; } - -q15_t CIO::getRxLevel() const -{ - return m_rxLevel; -} diff --git a/IO.h b/IO.h index f3ac4f9..07ed700 100644 --- a/IO.h +++ b/IO.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 @@ -56,8 +56,6 @@ public: void selfTest(); - q15_t getRxLevel() const; - private: bool m_started; diff --git a/SerialPort.cpp b/SerialPort.cpp index f729cb3..7a6e609 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -408,7 +408,7 @@ uint8_t CSerialPort::setFMParams2(const uint8_t* data, uint8_t length) uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) { - if (length < 10U) + if (length < 11U) return 4U; uint16_t timeout = data[0U] * 5U; @@ -425,8 +425,9 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) uint8_t rxBoost = data[8U]; uint8_t maxDev = data[9U]; + uint8_t rxLevel = data[10U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost, maxDev); + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost, maxDev, rxLevel); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From 4ffaa62855790d1efa359d8bed85440ec893ef7f Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 27 Apr 2020 11:16:06 +0200 Subject: [PATCH 102/119] Move division to CTCSSRX, change sample unscaling to 1/rxLevel --- FM.cpp | 17 ++--------------- FM.h | 2 -- FMCTCSSRX.cpp | 12 +++++++++--- FMCTCSSRX.h | 27 ++++++++++++++++++++------- 4 files changed, 31 insertions(+), 27 deletions(-) diff --git a/FM.cpp b/FM.cpp index 074de3d..75e12b3 100644 --- a/FM.cpp +++ b/FM.cpp @@ -55,7 +55,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) for (; i < length; i++) { q15_t currentSample = samples[i];//save to a local variable to avoid indirection on every access - CTCSSState ctcssState = m_ctcssRX.process(getUnscaledSample(currentSample)); + CTCSSState ctcssState = m_ctcssRX.process(currentSample); if (CTCSS_NOT_READY(ctcssState) && m_modemState != STATE_FM) { //Not enough samples to determine if you have CTCSS, just carry on @@ -158,7 +158,7 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_timeoutTone.setParams(timeoutLevel); m_blanking.setParams(maxDev, timeoutLevel); - uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold); + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, m_rxLevel); if (ret != 0U) return ret; @@ -400,17 +400,4 @@ void CFM::beginRelaying() m_ackMinTimer.start(); } -q15_t CFM::getUnscaledSample(q15_t sample) -{ - // sample / rxLevel - q31_t sample31 = q31_t(sample) << 16; - if (((sample31 >> 31) & 1) == ((m_rxLevel >> 15) & 1)) - sample31 += m_rxLevel >> 1; - else - sample31 -= m_rxLevel >> 1; - - sample31 /= m_rxLevel; - - return q15_t(sample31); -} diff --git a/FM.h b/FM.h index 1a07eb8..b9d6469 100644 --- a/FM.h +++ b/FM.h @@ -91,8 +91,6 @@ private: void sendCallsign(); void beginRelaying(); - - q15_t getUnscaledSample(q15_t sample); }; #endif diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 57fbfbe..180f1f3 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -86,12 +86,16 @@ m_threshold(0), m_count(0U), m_q0(0), m_q1(0), -m_result(CTS_NONE) +m_result(CTS_NONE), +m_rxLevelInverse(1) { } -uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) +uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, q15_t rxLevel) { + // Calculate 1/rxLevel + m_rxLevelInverse = q31_t(q15Division(65535 /* This value should be 32767 (q15 1). But this does not work.*/, rxLevel)); + m_coeffDivTwo = 0; for (uint8_t i = 0U; i < CTCSS_TABLE_DATA_LEN; i++) { @@ -111,6 +115,8 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold) CTCSSState CFMCTCSSRX::process(q15_t sample) { + q63_t sample31 = q31_t(sample) * m_rxLevelInverse; + m_result = m_result & (~CTS_READY); q31_t q2 = m_q1; @@ -122,7 +128,7 @@ CTCSSState CFMCTCSSRX::process(q15_t sample) q31_t t3 = t2 * 2; // m_q0 = m_coeffDivTwo * m_q1 * 2 - q2 + sample - m_q0 = t3 - q2 + q31_t(sample); + m_q0 = t3 - q2 + sample31; m_count++; if (m_count == N) { diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index a848cb9..7f36b32 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -53,7 +53,7 @@ class CFMCTCSSRX { public: CFMCTCSSRX(); - uint8_t setParams(uint8_t frequency, uint8_t threshold); + uint8_t setParams(uint8_t frequency, uint8_t threshold, q15_t rxLevel); CTCSSState process(q15_t sample); @@ -62,12 +62,25 @@ public: void reset(); private: - q63_t m_coeffDivTwo; - q31_t m_threshold; - uint16_t m_count; - q31_t m_q0; - q31_t m_q1; - CTCSSState m_result; + static inline q15_t q15Division(q15_t a, q15_t divisor) + { + q31_t a31 = q31_t(a) << 16; + + if (((a >> 31) & 1) == ((divisor >> 15) & 1)) + a31 += divisor >> 1; + else + a31 -= divisor >> 1; + + return a31 / divisor; + } + + q63_t m_coeffDivTwo; + q31_t m_threshold; + uint16_t m_count; + q31_t m_q0; + q31_t m_q1; + CTCSSState m_result; + q31_t m_rxLevelInverse; }; #endif From 93a8e6323317a801dd87b34f7d48ccde6bb30cae Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Mon, 27 Apr 2020 11:26:19 +0200 Subject: [PATCH 103/119] Remove m_rxLevel --- FM.cpp | 6 ++---- FM.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/FM.cpp b/FM.cpp index 75e12b3..85f9fb5 100644 --- a/FM.cpp +++ b/FM.cpp @@ -41,8 +41,7 @@ m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), m_useCOS(true), -m_rxBoost(1U), -m_rxLevel(128 * 128) +m_rxBoost(1U) { } @@ -149,7 +148,6 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque { m_useCOS = useCOS; m_rxBoost = q15_t(rxBoost); - m_rxLevel = q15_t(rxLevel * 128); m_timeoutTimer.setTimeout(timeout, 0U); m_kerchunkTimer.setTimeout(kerchunkTime, 0U); @@ -158,7 +156,7 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_timeoutTone.setParams(timeoutLevel); m_blanking.setParams(maxDev, timeoutLevel); - uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, m_rxLevel); + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, q15_t(rxLevel * 128)); if (ret != 0U) return ret; diff --git a/FM.h b/FM.h index b9d6469..c56d462 100644 --- a/FM.h +++ b/FM.h @@ -78,7 +78,6 @@ private: CFMBlanking m_blanking; bool m_useCOS; q15_t m_rxBoost; - q15_t m_rxLevel; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); From e8ed77241c5ef474fa9dff7846f7becf5bb86d02 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 27 Apr 2020 12:05:55 +0100 Subject: [PATCH 104/119] Simplify the calculations. --- FM.cpp | 4 +--- FMCTCSSRX.cpp | 7 +++---- FMCTCSSRX.h | 16 ++-------------- SerialPort.cpp | 2 +- 4 files changed, 7 insertions(+), 22 deletions(-) diff --git a/FM.cpp b/FM.cpp index 85f9fb5..4a2eb70 100644 --- a/FM.cpp +++ b/FM.cpp @@ -156,7 +156,7 @@ uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFreque m_timeoutTone.setParams(timeoutLevel); m_blanking.setParams(maxDev, timeoutLevel); - uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, q15_t(rxLevel * 128)); + uint8_t ret = m_ctcssRX.setParams(ctcssFrequency, ctcssThreshold, rxLevel); if (ret != 0U) return ret; @@ -397,5 +397,3 @@ void CFM::beginRelaying() m_timeoutTimer.start(); m_ackMinTimer.start(); } - - diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 180f1f3..00eebc6 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -91,10 +91,9 @@ m_rxLevelInverse(1) { } -uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, q15_t rxLevel) +uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, uint8_t level) { - // Calculate 1/rxLevel - m_rxLevelInverse = q31_t(q15Division(65535 /* This value should be 32767 (q15 1). But this does not work.*/, rxLevel)); + m_rxLevelInverse = 255 / q15_t(level); m_coeffDivTwo = 0; @@ -115,7 +114,7 @@ uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, q15_t rxLeve CTCSSState CFMCTCSSRX::process(q15_t sample) { - q63_t sample31 = q31_t(sample) * m_rxLevelInverse; + q31_t sample31 = q31_t(sample) * m_rxLevelInverse; m_result = m_result & (~CTS_READY); diff --git a/FMCTCSSRX.h b/FMCTCSSRX.h index 7f36b32..4c1dfc3 100644 --- a/FMCTCSSRX.h +++ b/FMCTCSSRX.h @@ -53,7 +53,7 @@ class CFMCTCSSRX { public: CFMCTCSSRX(); - uint8_t setParams(uint8_t frequency, uint8_t threshold, q15_t rxLevel); + uint8_t setParams(uint8_t frequency, uint8_t threshold, uint8_t level); CTCSSState process(q15_t sample); @@ -62,25 +62,13 @@ public: void reset(); private: - static inline q15_t q15Division(q15_t a, q15_t divisor) - { - q31_t a31 = q31_t(a) << 16; - - if (((a >> 31) & 1) == ((divisor >> 15) & 1)) - a31 += divisor >> 1; - else - a31 -= divisor >> 1; - - return a31 / divisor; - } - q63_t m_coeffDivTwo; q31_t m_threshold; uint16_t m_count; q31_t m_q0; q31_t m_q1; CTCSSState m_result; - q31_t m_rxLevelInverse; + q15_t m_rxLevelInverse; }; #endif diff --git a/SerialPort.cpp b/SerialPort.cpp index 7a6e609..da81dfd 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200426 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200427 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" From d08a3690b0716e9f3552b9b8d7408458eba81cc0 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 27 Apr 2020 13:07:03 +0100 Subject: [PATCH 105/119] Double the signal level to the CTCSS decoder. --- FMCTCSSRX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMCTCSSRX.cpp b/FMCTCSSRX.cpp index 00eebc6..9e81d3c 100644 --- a/FMCTCSSRX.cpp +++ b/FMCTCSSRX.cpp @@ -93,7 +93,7 @@ m_rxLevelInverse(1) uint8_t CFMCTCSSRX::setParams(uint8_t frequency, uint8_t threshold, uint8_t level) { - m_rxLevelInverse = 255 / q15_t(level); + m_rxLevelInverse = 511 / q15_t(level); m_coeffDivTwo = 0; From 5156ca13b722a501fbed475953bdcba79d2888f6 Mon Sep 17 00:00:00 2001 From: m0vse Date: Mon, 27 Apr 2020 14:18:42 +0100 Subject: [PATCH 106/119] Add FM Cal modes --- CalFM.cpp | 144 +++++++++++++++++++++++++++++++++++++++++ CalFM.h | 51 +++++++++++++++ Globals.h | 10 ++- MMDVM.cpp | 4 ++ MMDVM.ino | 4 ++ MMDVM_STM32F4xx.coproj | 4 +- SerialPort.cpp | 25 +++++-- 7 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 CalFM.cpp create mode 100644 CalFM.h 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) From b9bfc36e6fe78cac32469ca6e25527d024cb0d9a Mon Sep 17 00:00:00 2001 From: m0vse Date: Mon, 27 Apr 2020 14:32:16 +0100 Subject: [PATCH 107/119] Remove commented code --- CalFM.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/CalFM.cpp b/CalFM.cpp index 4a0b062..4215055 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -23,21 +23,6 @@ #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; From b2de5defa4e04fce5f31ebd28fdb978b44d38066 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 27 Apr 2020 14:47:17 +0100 Subject: [PATCH 108/119] Tweak the values slightly. --- CalFM.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CalFM.cpp b/CalFM.cpp index 4215055..ac9bd58 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -29,12 +29,11 @@ const struct TONE_TABLE { q31_t increment; } TONE_TABLE_DATA[] = { {2495U, 10U, 223248821}, - {2079U, 12U, 186025771}, - {1633U, 15U, 148802721}, - {1247U, 20U, 111579672}, - {1039U, 24U, 93012886}, - {956U, 26U, 85541432}}; - + {2079U, 12U, 186025772}, + {1633U, 15U, 146118367}, + {1247U, 19U, 111579672}, + {1039U, 23U, 93012886}, + {956U, 25U, 85541432}}; const uint8_t TONE_TABLE_DATA_LEN = 6U; @@ -126,4 +125,3 @@ uint8_t CCalFM::write(const uint8_t* data, uint8_t length) return 0U; } - From 30031d8e95f10e539f587b01934236c49ce32117 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 27 Apr 2020 14:53:41 +0100 Subject: [PATCH 109/119] Increase the output level. --- CalFM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CalFM.cpp b/CalFM.cpp index ac9bd58..0b09082 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -95,7 +95,7 @@ void CCalFM::process() 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); + q63_t value = ::arm_sin_q31(arg) * q63_t(m_level * 128); m_tone[i] = q15_t(__SSAT((value >> 31), 16)); arg += entry->increment; From 172ac615a870f9d1ab8bc7766f00e7b8a9160a12 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 27 Apr 2020 14:59:33 +0100 Subject: [PATCH 110/119] Rescale the output. --- CalFM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CalFM.cpp b/CalFM.cpp index 0b09082..58129bf 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -95,7 +95,7 @@ void CCalFM::process() q31_t arg = 0; for (uint16_t i = 0U; i < m_length; i++) { - q63_t value = ::arm_sin_q31(arg) * q63_t(m_level * 128); + q63_t value = ::arm_sin_q31(arg) * q63_t(m_level); m_tone[i] = q15_t(__SSAT((value >> 31), 16)); arg += entry->increment; From 849c2a0f50e2aafe8fa5d95fc9042bd1b283e34e Mon Sep 17 00:00:00 2001 From: m0vse Date: Tue, 28 Apr 2020 09:51:11 +0100 Subject: [PATCH 111/119] Change FM cal levels --- CalFM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CalFM.cpp b/CalFM.cpp index 58129bf..a654b48 100644 --- a/CalFM.cpp +++ b/CalFM.cpp @@ -41,7 +41,7 @@ CCalFM::CCalFM() : m_tone(NULL), m_frequency(0), m_length(0), -m_level(128*128), +m_level(128*32), m_transmit(false), m_lastState(STATE_IDLE), m_audioSeq(0) From dac0f26e5dcf6092a7cb483b2dd483f8960a36b8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 28 Apr 2020 14:43:40 +0100 Subject: [PATCH 112/119] Rename RXBoost to RFAudioBoost. --- FM.cpp | 10 +++++----- FM.h | 4 ++-- SerialPort.cpp | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/FM.cpp b/FM.cpp index 4a2eb70..7c90e01 100644 --- a/FM.cpp +++ b/FM.cpp @@ -41,7 +41,7 @@ m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), m_useCOS(true), -m_rxBoost(1U) +m_rfAudioBoost(1U) { } @@ -84,7 +84,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) else currentSample = 0U; - currentSample *= m_rxBoost; + currentSample *= m_rfAudioBoost; if (!m_callsign.isRunning()) currentSample += m_rfAck.getHighAudio(); @@ -144,10 +144,10 @@ uint8_t CFM::setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_ return m_rfAck.setParams(rfAck, speed, frequency, level, level); } -uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev, uint8_t rxLevel) +uint8_t CFM::setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel) { - m_useCOS = useCOS; - m_rxBoost = q15_t(rxBoost); + m_useCOS = useCOS; + m_rfAudioBoost = q15_t(rfAudioBoost); m_timeoutTimer.setTimeout(timeout, 0U); m_kerchunkTimer.setTimeout(kerchunkTime, 0U); diff --git a/FM.h b/FM.h index c56d462..d66f2ab 100644 --- a/FM.h +++ b/FM.h @@ -54,7 +54,7 @@ public: uint8_t setCallsign(const char* callsign, uint8_t speed, uint16_t frequency, uint8_t time, uint8_t holdoff, uint8_t highLevel, uint8_t lowLevel, bool callsignAtStart, bool callsignAtEnd); uint8_t setAck(const char* rfAck, uint8_t speed, uint16_t frequency, uint8_t minTime, uint16_t delay, uint8_t level); - uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rxBoost, uint8_t maxDev, uint8_t rxLevel); + uint8_t setMisc(uint16_t timeout, uint8_t timeoutLevel, uint8_t ctcssFrequency, uint8_t ctcssThreshold, uint8_t ctcssLevel, uint8_t kerchunkTime, uint8_t hangTime, bool useCOS, uint8_t rfAudioBoost, uint8_t maxDev, uint8_t rxLevel); private: CFMKeyer m_callsign; @@ -77,7 +77,7 @@ private: CFMDirectFormI m_filterStage3; CFMBlanking m_blanking; bool m_useCOS; - q15_t m_rxBoost; + q15_t m_rfAudioBoost; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/SerialPort.cpp b/SerialPort.cpp index 84282d8..7150695 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -101,7 +101,7 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #define HW_TYPE "MMDVM" #endif -#define DESCRIPTION "20200427 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" +#define DESCRIPTION "20200428 (D-Star/DMR/System Fusion/P25/NXDN/POCSAG/FM)" #if defined(GITVERSION) #define concat(h, a, b, c) h " " a " " b " GitID #" c "" @@ -423,11 +423,11 @@ uint8_t CSerialPort::setFMParams3(const uint8_t* data, uint8_t length) bool useCOS = (data[7U] & 0x01U) == 0x01U; - uint8_t rxBoost = data[8U]; + uint8_t rfAudioBoost = data[8U]; uint8_t maxDev = data[9U]; uint8_t rxLevel = data[10U]; - return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rxBoost, maxDev, rxLevel); + return fm.setMisc(timeout, timeoutLevel, ctcssFrequency, ctcssThreshold, ctcssLevel, kerchunkTime, hangTime, useCOS, rfAudioBoost, maxDev, rxLevel); } uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length) From cf18f95a924d264ecec4a54e041ca046d6b030d1 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 28 Apr 2020 21:37:06 +0200 Subject: [PATCH 113/119] Add Downsampling --- FM.cpp | 9 ++-- FM.h | 2 + FMDownsampleRB.cpp | 101 +++++++++++++++++++++++++++++++++++++++++++++ FMDownsampleRB.h | 68 ++++++++++++++++++++++++++++++ FMDownsampler.cpp | 63 ++++++++++++++++++++++++++++ FMDownsampler.h | 43 +++++++++++++++++++ 6 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 FMDownsampleRB.cpp create mode 100644 FMDownsampleRB.h create mode 100644 FMDownsampler.cpp create mode 100644 FMDownsampler.h diff --git a/FM.cpp b/FM.cpp index 7c90e01..3bc6a3b 100644 --- a/FM.cpp +++ b/FM.cpp @@ -41,7 +41,8 @@ m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), m_useCOS(true), -m_rfAudioBoost(1U) +m_rfAudioBoost(1U), +m_downsampler(1024)//Size might need adjustement { } @@ -74,8 +75,8 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) } else if (CTCSS_NOT_READY(ctcssState) && m_modemState == STATE_FM && i == length - 1) { //Not enough samples for CTCSS but we already are in FM, trigger the state machine //but do not trigger the state machine on every single sample, save CPU! - bool validCTCSS = CTCSS_VALID(ctcssState); - stateMachine(validCTCSS && cos, i + 1U); + bool validCTCSS = CTCSS_VALID(ctcssState); + stateMachine(validCTCSS && cos, i + 1U); } // Only let audio through when relaying audio @@ -101,6 +102,8 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)))); + m_downsampler.addSample(currentSample); + currentSample += m_ctcssTX.getAudio(); samples[i] = currentSample; diff --git a/FM.h b/FM.h index d66f2ab..fab8095 100644 --- a/FM.h +++ b/FM.h @@ -28,6 +28,7 @@ #include "FMKeyer.h" #include "FMTimer.h" #include "FMDirectForm1.h" +#include "FMDownsampler.h" enum FM_STATE { FS_LISTENING, @@ -78,6 +79,7 @@ private: CFMBlanking m_blanking; bool m_useCOS; q15_t m_rfAudioBoost; + CFMDownsampler m_downsampler; void stateMachine(bool validSignal, uint8_t length); void listeningState(bool validSignal); diff --git a/FMDownsampleRB.cpp b/FMDownsampleRB.cpp new file mode 100644 index 0000000..7d2cc2a --- /dev/null +++ b/FMDownsampleRB.cpp @@ -0,0 +1,101 @@ +/* + * 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 "FMDownsampleRB.h" + +CFMDownsampleRB::CFMDownsampleRB(uint16_t length) : +m_length(length), +m_head(0U), +m_tail(0U), +m_full(false), +m_overflow(false) +{ + m_samples = new uint8_t[length]; +} + +uint16_t CFMDownsampleRB::getSpace() const +{ + uint16_t n = 0U; + + if (m_tail == m_head) + n = m_full ? 0U : m_length; + else if (m_tail < m_head) + n = m_length - m_head + m_tail; + else + n = m_tail - m_head; + + if (n > m_length) + n = 0U; + + return n; +} + +uint16_t CFMDownsampleRB::getData() const +{ + if (m_tail == m_head) + return m_full ? m_length : 0U; + else if (m_tail < m_head) + return m_head - m_tail; + else + return m_length - m_tail + m_head; +} + +bool CFMDownsampleRB::put(uint8_t sample) +{ + if (m_full) { + m_overflow = true; + return false; + } + + m_samples[m_head] = sample; + + m_head++; + if (m_head >= m_length) + m_head = 0U; + + if (m_head == m_tail) + m_full = true; + + return true; +} + +bool CFMDownsampleRB::get(uint8_t& sample) +{ + if (m_head == m_tail && !m_full) + return false; + + sample = m_samples[m_tail]; + + m_full = false; + + m_tail++; + if (m_tail >= m_length) + m_tail = 0U; + + return true; +} + +bool CFMDownsampleRB::hasOverflowed() +{ + bool overflow = m_overflow; + + m_overflow = false; + + return overflow; +} diff --git a/FMDownsampleRB.h b/FMDownsampleRB.h new file mode 100644 index 0000000..6e8e466 --- /dev/null +++ b/FMDownsampleRB.h @@ -0,0 +1,68 @@ +/* + * 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(FMRB_H) +#define FMRB_H + +#if defined(STM32F4XX) +#include "stm32f4xx.h" +#elif defined(STM32F7XX) +#include "stm32f7xx.h" +#elif defined(STM32F105xC) +#include "stm32f1xx.h" +#include +#else +#include +#endif + +#if defined(__SAM3X8E__) || defined(STM32F105xC) +#define ARM_MATH_CM3 +#elif defined(STM32F7XX) +#define ARM_MATH_CM7 +#elif defined(STM32F4XX) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define ARM_MATH_CM4 +#else +#error "Unknown processor type" +#endif + +#include + +class CFMDownsampleRB { +public: + CFMDownsampleRB(uint16_t length); + + uint16_t getSpace() const; + + uint16_t getData() const; + + bool put(uint8_t sample); + + bool get(uint8_t& sample); + + bool hasOverflowed(); + +private: + uint16_t m_length; + volatile uint8_t* m_samples; + volatile uint16_t m_head; + volatile uint16_t m_tail; + volatile bool m_full; + bool m_overflow; +}; + +#endif diff --git a/FMDownsampler.cpp b/FMDownsampler.cpp new file mode 100644 index 0000000..51e9580 --- /dev/null +++ b/FMDownsampler.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA + * + * 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 "FMDownsampler.h" + + +CFMDownsampler::CFMDownsampler(uint16_t length) : +m_ringBuffer(length),//length might need tweaking +m_packIndex(0), +m_downSampleIndex(0) +{ + m_samplePack = 0; +} + +void CFMDownsampler::addSample(q15_t sample) +{ + //only take one of three samples + if(m_downSampleIndex == 0) { + switch(m_packIndex){ + case 0: + m_samplePack = int32_t(sample) << 12; + break; + case 1:{ + m_samplePack |= int32_t(sample); + + //we did not use MSB; skip it + m_ringBuffer.put(m_samplePackBytes[1]); + m_ringBuffer.put(m_samplePackBytes[2]); + m_ringBuffer.put(m_samplePackBytes[3]); + + m_samplePack = 0; + } + break; + default: + //should never happen + break; + } + m_packIndex++; + if(m_packIndex >= 2) + m_packIndex = 0; + } + + m_downSampleIndex++; + if(m_downSampleIndex >= 3) + m_downSampleIndex = 0; +} \ No newline at end of file diff --git a/FMDownsampler.h b/FMDownsampler.h new file mode 100644 index 0000000..fd17e0a --- /dev/null +++ b/FMDownsampler.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck F4FXL - KC3FRA + * + * 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(FMDOWNSAMPLER_H) +#define FMDOWNSAMPLER_H + +#include "Config.h" +#include "FMDownsampleRB.h" + +class CFMDownsampler { +public: + CFMDownsampler(uint16_t length); + void addSample(q15_t sample); + inline bool getPackedData(uint8_t& data){ return m_ringBuffer.get(data); }; + inline bool hasOverflowed() { return m_ringBuffer.hasOverflowed(); }; + +private: + CFMDownsampleRB m_ringBuffer; + union { + int32_t m_samplePack; + int8_t m_samplePackBytes[4]; + }; + uint8_t m_packIndex; + uint8_t m_downSampleIndex; +}; + +#endif \ No newline at end of file From 4b267c2e1e98a321733784335d6a1cd4ccdc47c8 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Tue, 28 Apr 2020 22:35:06 +0200 Subject: [PATCH 114/119] Move downsampling tap point --- FM.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/FM.cpp b/FM.cpp index 3bc6a3b..2bed736 100644 --- a/FM.cpp +++ b/FM.cpp @@ -78,10 +78,12 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) bool validCTCSS = CTCSS_VALID(ctcssState); stateMachine(validCTCSS && cos, i + 1U); } - + // Only let audio through when relaying audio - if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) + if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { + m_downsampler.addSample(currentSample); currentSample = m_blanking.process(currentSample); + } else currentSample = 0U; @@ -100,9 +102,7 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) if (!m_callsign.isRunning() && !m_rfAck.isRunning()) currentSample += m_timeoutTone.getAudio(); - currentSample = q15_t(m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample)))); - - m_downsampler.addSample(currentSample); + currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample))); currentSample += m_ctcssTX.getAudio(); From 626a280d276390c89af30de9e3d081d06c8aed72 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 29 Apr 2020 18:24:15 +0200 Subject: [PATCH 115/119] Add filter chrachertitics comment --- FM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FM.cpp b/FM.cpp index 2bed736..fa5fd53 100644 --- a/FM.cpp +++ b/FM.cpp @@ -36,7 +36,7 @@ m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer(), -m_filterStage1( 724, 1448, 724, 32768, -37895, 21352), +m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Filter 300 to 2700Hz, 0.2dB passband ripple m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), m_blanking(), From da87afc8d416d26cbfbd8310e0dd413198f5c700 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 29 Apr 2020 20:41:41 +0200 Subject: [PATCH 116/119] Move helper script to separate folder --- .../FMGenerateFilterCoefficients.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename FMGenerateFilterCoefficients.py => Tools/FMGenerateFilterCoefficients.py (66%) diff --git a/FMGenerateFilterCoefficients.py b/Tools/FMGenerateFilterCoefficients.py similarity index 66% rename from FMGenerateFilterCoefficients.py rename to Tools/FMGenerateFilterCoefficients.py index 84ea62b..0580cdf 100644 --- a/FMGenerateFilterCoefficients.py +++ b/Tools/FMGenerateFilterCoefficients.py @@ -17,21 +17,22 @@ f2 = 2700 rp = 0.2 # scaling factor in bits, do not change ! -q = 15 +q = 0 # scaling factor as facor... scaling_factor = 2**q # let's generate a sequence of 2nd order IIR filters -#sos = signal.butter(2,[f1/fs*2,f2/fs*2],'pass',output='sos') -sos = signal.cheby1(3,rp,[f1/fs*2,f2/fs*2],'bandpass', output='sos') +#sos = signal.cheby1(3,rp,[f1, f2],'bandpass', output='sos', fs=fs) +#sos = signal.cheby1(1, rp, 2122, 'lowpass', output='sos', fs=fs) #deemphasis filter +sos = signal.cheby1(1, rp, 2122, 'highpass', output='sos', fs=fs) #deemphasis filter -sos = np.round((sos) * scaling_factor) +#sos = np.round((sos) * scaling_factor) # print coefficients for biquad in sos: for coeff in biquad: - print(int(coeff),",",sep="",end="") - #print((coeff),",",sep="",end="") + #print(int(coeff),",",sep="",end="") + print((coeff),",",sep="",end="") print("") # plot the frequency response From b23620ede3850af72c295e3230b0624875d3ccbe Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 29 Apr 2020 20:42:20 +0200 Subject: [PATCH 117/119] Add de and pre emphasis --- FM.cpp | 8 ++++++- FM.h | 2 ++ FMDirectForm1.h | 2 +- Tools/emphasis.txt | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 Tools/emphasis.txt diff --git a/FM.cpp b/FM.cpp index fa5fd53..2258109 100644 --- a/FM.cpp +++ b/FM.cpp @@ -36,9 +36,11 @@ m_kerchunkTimer(), m_ackMinTimer(), m_ackDelayTimer(), m_hangTimer(), -m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Filter 300 to 2700Hz, 0.2dB passband ripple +m_filterStage1( 724, 1448, 724, 32768, -37895, 21352),//3rd order Cheby Filter 300 to 2700Hz, 0.2dB passband ripple, sampling rate 24kHz m_filterStage2(32768, 0,-32768, 32768, -50339, 19052), m_filterStage3(32768, -65536, 32768, 32768, -64075, 31460), +m_preemphasis(32768, 13967, 0, 32768, -18801, 0),//75µS 24kHz sampling rate +m_deemphasis (32768, -18801, 0, 32768, 13967, 0),//75µS 24kHz sampling rate m_blanking(), m_useCOS(true), m_rfAudioBoost(1U), @@ -79,6 +81,8 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) stateMachine(validCTCSS && cos, i + 1U); } + currentSample = m_deemphasis.filter(currentSample); + // Only let audio through when relaying audio if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { m_downsampler.addSample(currentSample); @@ -104,6 +108,8 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) currentSample = m_filterStage3.filter(m_filterStage2.filter(m_filterStage1.filter(currentSample))); + currentSample = m_preemphasis.filter(currentSample); + currentSample += m_ctcssTX.getAudio(); samples[i] = currentSample; diff --git a/FM.h b/FM.h index fab8095..a852a80 100644 --- a/FM.h +++ b/FM.h @@ -76,6 +76,8 @@ private: CFMDirectFormI m_filterStage1; CFMDirectFormI m_filterStage2; CFMDirectFormI m_filterStage3; + CFMDirectFormI m_preemphasis; + CFMDirectFormI m_deemphasis; CFMBlanking m_blanking; bool m_useCOS; q15_t m_rfAudioBoost; diff --git a/FMDirectForm1.h b/FMDirectForm1.h index 4b90245..450274d 100644 --- a/FMDirectForm1.h +++ b/FMDirectForm1.h @@ -79,7 +79,7 @@ public: inline q15_t filter(const q15_t in) { // calculate the output - register q31_t out_upscaled = c_b0 * in //F4FXL puting stauration here made everything quiet, not sure why + register q31_t out_upscaled = c_b0 * in + c_b1 * m_x1 + c_b2 * m_x2 - c_a1 * m_y1 diff --git a/Tools/emphasis.txt b/Tools/emphasis.txt new file mode 100644 index 0000000..39660d1 --- /dev/null +++ b/Tools/emphasis.txt @@ -0,0 +1,58 @@ +% GNU Octave script to generate pre and deemphasis filters +% https://dsp.stackexchange.com/questions/34605/biquad-cookbook-formula-for-broadcast-fm-de-emphasis +% PACKAGES + +pkg load control +pkg load signal + +clear all; +clc; + +fs = 24000; +samplingtime = 1/fs; + +% analog prototype +A2 = [1]; +B2 = [0.000075 1]; + +% Pre +Ds = tf(B2, A2); +% De +% Ds = tf(A2, B2); + +Ds = Ds/dcgain(Ds); + +% MZT +T1 = 0.000075; % 75us +z1 = -exp(-1.0/(fs*T1)); +p1 = 1+z1; + +a0 = 1.0; +a1 = p1; +a2 = 0; + +b0 = 1.0; +b1 = z1; +b2 = 0; + +% swap between a1, b1 to select pre- or de-emphasis + +# Pre +Bmzt = [b0 a1 b2] +Amzt = [a0 b1 a2] +% De +% Bmzt = [b0 b1 b2] +% Amzt = [a0 a1 a2] + +DzMZT = tf(Amzt, Bmzt, samplingtime); +DzMZT = DzMZT/dcgain(DzMZT); + +%% Plot +wmin = 2 * pi * 20.0; % 20Hz +wmax = 2 * pi * ((fs/2.0) - (fs/2 - 20000)); %20kHz + +figure(1); +bode(Ds, 'b', DzMZT, 'c', {wmin, wmax}); +legend('Analog prototype', 'MZT 2nd order','location', 'northwest'); +grid on; +pause(); From 373aed13c83256224b57f33249e6c36e47ef0492 Mon Sep 17 00:00:00 2001 From: Geoffrey Merck Date: Wed, 29 Apr 2020 20:43:01 +0200 Subject: [PATCH 118/119] Restore Passband --- Tools/FMGenerateFilterCoefficients.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/FMGenerateFilterCoefficients.py b/Tools/FMGenerateFilterCoefficients.py index 0580cdf..7d8a3db 100644 --- a/Tools/FMGenerateFilterCoefficients.py +++ b/Tools/FMGenerateFilterCoefficients.py @@ -22,9 +22,9 @@ q = 0 scaling_factor = 2**q # let's generate a sequence of 2nd order IIR filters -#sos = signal.cheby1(3,rp,[f1, f2],'bandpass', output='sos', fs=fs) +sos = signal.cheby1(3,rp,[f1, f2],'bandpass', output='sos', fs=fs) #sos = signal.cheby1(1, rp, 2122, 'lowpass', output='sos', fs=fs) #deemphasis filter -sos = signal.cheby1(1, rp, 2122, 'highpass', output='sos', fs=fs) #deemphasis filter +#sos = signal.cheby1(1, rp, 2122, 'highpass', output='sos', fs=fs) #deemphasis filter #sos = np.round((sos) * scaling_factor) From 200199c5d492123ee95c2a56ae28d5c1c5d986c8 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 29 Apr 2020 20:59:48 +0100 Subject: [PATCH 119/119] Reduce the CPU load slightly. --- FM.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/FM.cpp b/FM.cpp index 2258109..fd18f11 100644 --- a/FM.cpp +++ b/FM.cpp @@ -81,17 +81,15 @@ void CFM::samples(bool cos, q15_t* samples, uint8_t length) stateMachine(validCTCSS && cos, i + 1U); } - currentSample = m_deemphasis.filter(currentSample); - // Only let audio through when relaying audio if (m_state == FS_RELAYING || m_state == FS_KERCHUNK) { + currentSample = m_deemphasis.filter(currentSample); m_downsampler.addSample(currentSample); currentSample = m_blanking.process(currentSample); - } - else + currentSample *= m_rfAudioBoost; + } else { currentSample = 0U; - - currentSample *= m_rfAudioBoost; + } if (!m_callsign.isRunning()) currentSample += m_rfAck.getHighAudio();