/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2020 Edouard Griffiths, F4EXB // // // // 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 as version 3 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 V3 for more details. // // // // You should have received a copy of the GNU General Public License // // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// #include #include "chirpchatmodsource.h" const int ChirpChatModSource::m_levelNbSamples = 480; // every 10ms ChirpChatModSource::ChirpChatModSource() : m_channelSampleRate(48000), m_channelFrequencyOffset(0), m_phaseIncrements(nullptr), m_repeatCount(0), m_active(false), m_modPhasor(0.0f), m_levelCalcCount(0), m_peakLevel(0.0f), m_levelSum(0.0f) { m_magsq = 0.0; initSF(m_settings.m_spreadFactor); initTest(m_settings.m_spreadFactor, m_settings.m_deBits); reset(); applySettings(m_settings, true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); } ChirpChatModSource::~ChirpChatModSource() { delete[] m_phaseIncrements; } void ChirpChatModSource::initSF(unsigned int sf) { m_fftLength = 1 << sf; m_state = ChirpChatStateIdle; m_quarterSamples = (m_fftLength/4)*ChirpChatModSettings::oversampling; float halfAngle = M_PI/ChirpChatModSettings::oversampling; float phase = -halfAngle; if (m_phaseIncrements) { delete[] m_phaseIncrements; } m_phaseIncrements = new double[2*m_fftLength*ChirpChatModSettings::oversampling]; phase = -halfAngle; for (unsigned int i = 0; i < m_fftLength*ChirpChatModSettings::oversampling; i++) { m_phaseIncrements[i] = phase; phase += (2*halfAngle) / (m_fftLength*ChirpChatModSettings::oversampling); } std::copy( m_phaseIncrements, m_phaseIncrements+m_fftLength*ChirpChatModSettings::oversampling, m_phaseIncrements+m_fftLength*ChirpChatModSettings::oversampling ); } void ChirpChatModSource::initTest(unsigned int sf, unsigned int deBits) { unsigned int fftLength = 1< 1.0f) // decimate { modulateSample(); while (!m_interpolator.decimate(&m_interpolatorDistanceRemain, m_modSample, &ci)) { modulateSample(); } } else { if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, m_modSample, &ci)) { modulateSample(); } } m_interpolatorDistanceRemain += m_interpolatorDistance; ci *= m_carrierNco.nextIQ(); // shift to carrier frequency if (!(m_state == ChirpChatStateIdle)) { double magsq = std::norm(ci); magsq /= (SDR_TX_SCALED*SDR_TX_SCALED); m_movingAverage(magsq); m_magsq = m_movingAverage.asDouble(); } sample.m_real = (FixReal) ci.real(); sample.m_imag = (FixReal) ci.imag(); } void ChirpChatModSource::modulateSample() { if (m_state == ChirpChatStateIdle) { m_modSample = Complex{0.0, 0.0}; m_sampleCounter++; if (m_sampleCounter == m_quietSamples*ChirpChatModSettings::oversampling) // done with quiet time { m_chirp0 = 0; m_chirp = m_fftLength*ChirpChatModSettings::oversampling - 1; if (m_symbols.size() != 0) // some payload to transmit { if (m_settings.m_messageRepeat == 0) // infinite { m_state = ChirpChatStatePreamble; m_active = true; } else { if (m_repeatCount != 0) { m_repeatCount--; m_state = ChirpChatStatePreamble; m_active = true; } else { m_active = false; } } } else { m_active = false; } } } else if (m_state == ChirpChatStatePreamble) { m_modPhasor += m_phaseIncrements[m_chirp]; // up chirps m_modSample = Complex(std::polar(0.891235351562 * SDR_TX_SCALED, m_modPhasor)); m_fftCounter++; if (m_fftCounter == m_fftLength*ChirpChatModSettings::oversampling) { m_chirpCount++; m_fftCounter = 0; if (m_chirpCount == m_settings.m_preambleChirps) { m_chirpCount = 0; if (m_settings.hasSyncWord()) { m_chirp0 = ((m_settings.m_syncWord >> ((1-m_chirpCount)*4)) & 0xf)*8; m_chirp = (m_chirp0 + m_fftLength)*ChirpChatModSettings::oversampling - 1; m_state = ChirpChatStateSyncWord; } else { m_sampleCounter = 0; m_chirp0 = 0; m_chirp = m_fftLength*ChirpChatModSettings::oversampling - 1; m_state = ChirpChatStateSFD; } } } } else if (m_state == ChirpChatStateSyncWord) { m_modPhasor += m_phaseIncrements[m_chirp]; // up chirps m_modSample = Complex(std::polar(0.891235351562 * SDR_TX_SCALED, m_modPhasor)); m_fftCounter++; if (m_fftCounter == m_fftLength*ChirpChatModSettings::oversampling) { m_chirpCount++; m_chirp0 = ((m_settings.m_syncWord >> ((1-m_chirpCount)*4)) & 0xf)*8; m_chirp = (m_chirp0 + m_fftLength)*ChirpChatModSettings::oversampling - 1; m_fftCounter = 0; if (m_chirpCount == 2) { m_sampleCounter = 0; m_chirpCount = 0; m_chirp0 = 0; m_chirp = m_fftLength*ChirpChatModSettings::oversampling - 1; m_state = ChirpChatStateSFD; } } } else if (m_state == ChirpChatStateSFD) { m_modPhasor -= m_phaseIncrements[m_chirp]; // down chirps m_modSample = Complex(std::polar(0.891235351562 * SDR_TX_SCALED, m_modPhasor)); m_fftCounter++; m_sampleCounter++; if (m_fftCounter == m_fftLength*ChirpChatModSettings::oversampling) { m_chirp0 = 0; m_chirp = m_fftLength*ChirpChatModSettings::oversampling - 1; m_fftCounter = 0; } if (m_sampleCounter == m_quarterSamples) { m_chirpCount++; m_sampleCounter = 0; } if (m_chirpCount == m_settings.getNbSFDFourths()) { m_fftCounter = 0; m_chirpCount = 0; m_chirp0 = encodeSymbol(m_symbols[m_chirpCount]); m_chirp = (m_chirp0 + m_fftLength)*ChirpChatModSettings::oversampling - 1; m_state = ChirpChatStatePayload; } } else if (m_state == ChirpChatStatePayload) { m_modPhasor += m_phaseIncrements[m_chirp]; // up chirps m_modSample = Complex(std::polar(0.891235351562 * SDR_TX_SCALED, m_modPhasor)); m_fftCounter++; if (m_fftCounter == m_fftLength*ChirpChatModSettings::oversampling) { m_chirpCount++; if (m_chirpCount == m_symbols.size()) { reset(); m_state = ChirpChatStateIdle; } else { m_chirp0 = encodeSymbol(m_symbols[m_chirpCount]); m_chirp = (m_chirp0 + m_fftLength)*ChirpChatModSettings::oversampling - 1; m_fftCounter = 0; } } } // limit phasor range to ]-pi,pi] if (m_modPhasor > M_PI) { m_modPhasor -= (2.0f * M_PI); } m_chirp++; if (m_chirp >= (m_chirp0 + m_fftLength)*ChirpChatModSettings::oversampling) { m_chirp = m_chirp0*ChirpChatModSettings::oversampling; } } unsigned short ChirpChatModSource::encodeSymbol(unsigned short symbol) { if (m_settings.m_deBits == 0) { return symbol; } unsigned int deWidth = 1<& symbols) { m_symbols = symbols; qDebug("ChirpChatModSource::setSymbols: m_symbols: %lu", m_symbols.size()); m_repeatCount = m_settings.m_messageRepeat; m_state = ChirpChatStateIdle; // first reset to idle reset(); m_sampleCounter = m_quietSamples*ChirpChatModSettings::oversampling - 1; // start immediately }