Compare commits

..

7 Commits

@ -41,6 +41,13 @@ jobs:
rm -f dvm-firmware-hs_f1.bin rm -f dvm-firmware-hs_f1.bin
make -f Makefile.STM32FX mmdvm-hs-hat-dual make -f Makefile.STM32FX mmdvm-hs-hat-dual
mv -f dvm-firmware-hs_f1.bin dvm-firmware-hs-hat-dual.bin mv -f dvm-firmware-hs_f1.bin dvm-firmware-hs-hat-dual.bin
- name: Build STM32FX mmdvm-hs-hat-usb-dual Firmware
run: |
rm -rf obj_f1
rm -rf obj_f4
rm -f dvm-firmware-hs_f1bl.bin
make -f Makefile.STM32FX mmdvm-hs-hat-usb-dual
mv -f dvm-firmware-hs_f1bl.bin dvm-firmware-hs-hat-dual_usb.bin
- name: Build STM32FX mmdvm-hs-hat Firmware - name: Build STM32FX mmdvm-hs-hat Firmware
run: | run: |
rm -rf obj_f1 rm -rf obj_f1
@ -48,6 +55,13 @@ jobs:
rm -f dvm-firmware-hs_f1.bin rm -f dvm-firmware-hs_f1.bin
make -f Makefile.STM32FX mmdvm-hs-hat make -f Makefile.STM32FX mmdvm-hs-hat
mv -f dvm-firmware-hs_f1.bin dvm-firmware-hs-hat.bin mv -f dvm-firmware-hs_f1.bin dvm-firmware-hs-hat.bin
- name: Build STM32FX mmdvm-hs-hat-usb Firmware
run: |
rm -rf obj_f1
rm -rf obj_f4
rm -f dvm-firmware-hs_f1bl.bin
make -f Makefile.STM32FX mmdvm-hs-hat-usb
mv -f dvm-firmware-hs_f1bl.bin dvm-firmware-hs-hat_usb.bin
- name: Firmware Hash - name: Firmware Hash
run: | run: |
@ -62,11 +76,23 @@ jobs:
sha1 : $(sha1sum dvm-firmware-hs-hat-dual.bin) sha1 : $(sha1sum dvm-firmware-hs-hat-dual.bin)
sha256: $(sha256sum dvm-firmware-hs-hat-dual.bin) sha256: $(sha256sum dvm-firmware-hs-hat-dual.bin)
dvm-firmware-hs-hat-dual_usb.bin
size : $(wc -c dvm-firmware-hs-hat-dual_usb.bin)
md5 : $(md5sum dvm-firmware-hs-hat-dual_usb.bin)
sha1 : $(sha1sum dvm-firmware-hs-hat-dual_usb.bin)
sha256: $(sha256sum dvm-firmware-hs-hat-dual_usb.bin)
dvm-firmware-hs-hat.bin dvm-firmware-hs-hat.bin
size : $(wc -c dvm-firmware-hs-hat.bin) size : $(wc -c dvm-firmware-hs-hat.bin)
md5 : $(md5sum dvm-firmware-hs-hat.bin) md5 : $(md5sum dvm-firmware-hs-hat.bin)
sha1 : $(sha1sum dvm-firmware-hs-hat.bin) sha1 : $(sha1sum dvm-firmware-hs-hat.bin)
sha256: $(sha256sum dvm-firmware-hs-hat.bin) sha256: $(sha256sum dvm-firmware-hs-hat.bin)
dvm-firmware-hs-hat_usb.bin
size : $(wc -c dvm-firmware-hs-hat_usb.bin)
md5 : $(md5sum dvm-firmware-hs-hat_usb.bin)
sha1 : $(sha1sum dvm-firmware-hs-hat_usb.bin)
sha256: $(sha256sum dvm-firmware-hs-hat_usb.bin)
EOF EOF
echo '```' >> release.txt echo '```' >> release.txt
@ -77,4 +103,6 @@ jobs:
body_path: release.txt body_path: release.txt
files: | files: |
dvm-firmware-hs-hat-dual.bin dvm-firmware-hs-hat-dual.bin
dvm-firmware-hs-hat-dual_usb.bin
dvm-firmware-hs-hat.bin dvm-firmware-hs-hat.bin
dvm-firmware-hs-hat_usb.bin

@ -1,26 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* * Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2020,2021 Jonathan Naylor, G4KLX * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* Copyright (C) 2016 Jim McLaughlin, KI6ZUM *
* Copyright (C) 2016,2017,2018,2019,2020 Andy Uribe, CA6JAU * Some of the code is based on work of Guus Van Dooren PE1PLM:
* Copyright (C) 2017 Danilo, DB4PLE * https://github.com/ki6zum/gmsk-dstar/blob/master/firmware/dvmega/dvmega.ino
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL *
* * 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 <math.h> #include <math.h>
#include "Globals.h" #include "Globals.h"
#include "ADF7021.h" #include "ADF7021.h"
/*
* Some of the code is based on work of Guus Van Dooren PE1PLM:
* https://github.com/ki6zum/gmsk-dstar/blob/master/firmware/dvmega/dvmega.ino
*/
#if defined(ENABLE_ADF7021) #if defined(ENABLE_ADF7021)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -70,8 +88,9 @@ uint8_t m_afcRange;
// Global Functions // Global Functions
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* */ /// <summary>
///
/// </summary>
static void AD7021_IOCTL_Shift() static void AD7021_IOCTL_Shift()
{ {
for (int i = 31; i >= 0; i--) { for (int i = 31; i >= 0; i--) {
@ -90,8 +109,9 @@ static void AD7021_IOCTL_Shift()
io.SDATA(LOW); io.SDATA(LOW);
} }
/* */ /// <summary>
///
/// </summary>
static void AD7021_IOCTL_SLEPulse() static void AD7021_IOCTL_SLEPulse()
{ {
io.SLE1(HIGH); io.SLE1(HIGH);
@ -99,8 +119,10 @@ static void AD7021_IOCTL_SLEPulse()
io.SLE1(LOW); io.SLE1(LOW);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="doSle"></param>
static void AD7021_1_IOCTL(bool doSle = true) static void AD7021_1_IOCTL(bool doSle = true)
{ {
AD7021_IOCTL_Shift(); AD7021_IOCTL_Shift();
@ -110,8 +132,9 @@ static void AD7021_1_IOCTL(bool doSle = true)
} }
#if defined(DUPLEX) #if defined(DUPLEX)
/* */ /// <summary>
///
/// </summary>
static void AD7021_2_IOCTL_SLEPulse() static void AD7021_2_IOCTL_SLEPulse()
{ {
io.SLE2(HIGH); io.SLE2(HIGH);
@ -119,8 +142,10 @@ static void AD7021_2_IOCTL_SLEPulse()
io.SLE2(LOW); io.SLE2(LOW);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="doSle"></param>
static void AD7021_2_IOCTL(bool doSle = true) static void AD7021_2_IOCTL(bool doSle = true)
{ {
AD7021_IOCTL_Shift(); AD7021_IOCTL_Shift();
@ -134,8 +159,9 @@ static void AD7021_2_IOCTL(bool doSle = true)
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Hardware interrupt handler. */ /// <summary>
/// Hardware interrupt handler.
/// </summary>
void IO::interrupt1() void IO::interrupt1()
{ {
uint8_t bit = 0U; uint8_t bit = 0U;
@ -244,8 +270,9 @@ void IO::interrupt1()
} }
#if defined(DUPLEX) #if defined(DUPLEX)
/* Hardware interrupt handler. */ /// <summary>
/// Hardware interrupt handler.
/// </summary>
void IO::interrupt2() void IO::interrupt2()
{ {
uint8_t bit = 0U; uint8_t bit = 0U;
@ -263,13 +290,16 @@ void IO::interrupt2()
} }
#endif #endif
/* Sets the ADF7021 RF configuration. */ /// <summary>
/// Sets the ADF7021 RF configuration.
/// </summary>
/// <param name="modemState"></param>
/// <param name="reset"></param>
void IO::rf1Conf(DVM_STATE modemState, bool reset) void IO::rf1Conf(DVM_STATE modemState, bool reset)
{ {
uint32_t txFrequencyTmp, rxFrequencyTmp; uint32_t txFrequencyTmp, rxFrequencyTmp;
DEBUG4("IO::rf1Conf() ADF1 (Tx/Rx); modemState/reset/rxGain", modemState, reset, m_gainMode); DEBUG4("IO::rf1Conf(): configuring ADF for Tx/Rx; modemState/reset/rxGain", modemState, reset, m_gainMode);
#if defined (ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #if defined (ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS)
io.checkBand(m_rxFrequency, m_txFrequency); io.checkBand(m_rxFrequency, m_txFrequency);
@ -348,16 +378,12 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
AD7021_CONTROL = ADF7021_REG3; AD7021_CONTROL = ADF7021_REG3;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG3("IO::rf1Conf() ADF1 REG3 =", (ADF7021_REG3 >> 16 & 0xFFFFU), (ADF7021_REG3 & 0xFFFFU));
/* /*
** Demodulator Setup (Register 4) ** Demodulator Setup (Register 4)
*/ */
AD7021_CONTROL = ADF7021_REG4; AD7021_CONTROL = ADF7021_REG4;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG3("IO::rf1Conf() ADF1 REG4 =", (ADF7021_REG4 >> 16 & 0xFFFFU), (ADF7021_REG4 & 0xFFFFU));
/* /*
** IF Fine Cal Setup (Register 6) ** IF Fine Cal Setup (Register 6)
*/ */
@ -384,8 +410,6 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
AD7021_CONTROL = ADF7021_REG2; AD7021_CONTROL = ADF7021_REG2;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG3("IO::rf1Conf() ADF1 REG2 =", (ADF7021_REG2 >> 16 & 0xFFFFU), (ADF7021_REG3 & 0xFFFFU));
/* /*
** Test DAC (Register 14) ** Test DAC (Register 14)
*/ */
@ -422,8 +446,6 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
AD7021_CONTROL = ADF7021_REG10; AD7021_CONTROL = ADF7021_REG10;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG3("IO::rf1Conf() ADF1 REG10 =", (ADF7021_REG10 >> 16 & 0xFFFFU), (ADF7021_REG10 & 0xFFFFU));
/* /*
** Sync Word Detect (Register 11) ** Sync Word Detect (Register 11)
*/ */
@ -442,8 +464,6 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
AD7021_CONTROL = ADF7021_REG13; AD7021_CONTROL = ADF7021_REG13;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG3("IO::rf1Conf() ADF1 REG13 =", (ADF7021_REG13 >> 16 & 0xFFFFU), (ADF7021_REG13 & 0xFFFFU));
/* /*
** Test Mode (Register 15) ** Test Mode (Register 15)
*/ */
@ -466,11 +486,14 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
} }
#if defined(DUPLEX) #if defined(DUPLEX)
/* Sets the ADF7021 RF configuration. */ /// <summary>
/// Sets the ADF7021 RF configuration.
/// </summary>
/// <param name="modemState"></param>
/// <param name="reset"></param>
void IO::rf2Conf(DVM_STATE modemState) void IO::rf2Conf(DVM_STATE modemState)
{ {
DEBUG3("IO::rf2Conf() ADF2 (Rx); modemState/rxGain", modemState, m_gainMode); DEBUG3("IO::rf2Conf(): configuring 2nd ADF for Rx; modemState/rxGain", modemState, m_gainMode);
// configure ADF Tx/RX // configure ADF Tx/RX
configureTxRx(modemState); configureTxRx(modemState);
@ -488,16 +511,12 @@ void IO::rf2Conf(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG3; AD7021_CONTROL = ADF7021_REG3;
AD7021_2_IOCTL(); AD7021_2_IOCTL();
DEBUG3("IO::rf2Conf() ADF2 REG3 =", (ADF7021_REG3 >> 16 & 0xFFFFU), (ADF7021_REG3 & 0xFFFFU));
/* /*
** Demodulator Setup (Register 4) ** Demodulator Setup (Register 4)
*/ */
AD7021_CONTROL = ADF7021_REG4; AD7021_CONTROL = ADF7021_REG4;
AD7021_2_IOCTL(); AD7021_2_IOCTL();
DEBUG3("IO::rf2Conf() ADF2 REG4 =", (ADF7021_REG4 >> 16 & 0xFFFFU), (ADF7021_REG4 & 0xFFFFU));
/* /*
** IF Fine Cal Setup (Register 6) ** IF Fine Cal Setup (Register 6)
*/ */
@ -526,8 +545,6 @@ void IO::rf2Conf(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG2; AD7021_CONTROL = ADF7021_REG2;
AD7021_2_IOCTL(); AD7021_2_IOCTL();
DEBUG3("IO::rf2Conf() ADF2 REG2 =", (ADF7021_REG2 >> 16 & 0xFFFFU), (ADF7021_REG3 & 0xFFFFU));
/* /*
** Test DAC (Register 14) ** Test DAC (Register 14)
*/ */
@ -560,8 +577,6 @@ void IO::rf2Conf(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG10; AD7021_CONTROL = ADF7021_REG10;
AD7021_2_IOCTL(); AD7021_2_IOCTL();
DEBUG3("IO::rf2Conf() ADF2 REG10 =", (ADF7021_REG10 >> 16 & 0xFFFFU), (ADF7021_REG10 & 0xFFFFU));
/* /*
** Sync Word Detect (Register 11) ** Sync Word Detect (Register 11)
*/ */
@ -580,8 +595,6 @@ void IO::rf2Conf(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG13; AD7021_CONTROL = ADF7021_REG13;
AD7021_2_IOCTL(); AD7021_2_IOCTL();
DEBUG3("IO::rf2Conf() ADF2 REG13 =", (ADF7021_REG13 >> 16 & 0xFFFFU), (ADF7021_REG13 & 0xFFFFU));
/* /*
** Test Mode (Register 15) ** Test Mode (Register 15)
*/ */
@ -590,8 +603,12 @@ void IO::rf2Conf(DVM_STATE modemState)
} }
#endif // DUPLEX #endif // DUPLEX
/* Sets the deviation levels. */ /// <summary>
///
/// </summary>
/// <param name="dmrTXLevel"></param>
/// <param name="p25TXLevel"></param>
/// <param name="nxdnTXLevel"></param>
void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel) void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel)
{ {
dmrDev = uint16_t((ADF7021_DEV_DMR * uint16_t(dmrTXLevel)) / 128U); dmrDev = uint16_t((ADF7021_DEV_DMR * uint16_t(dmrTXLevel)) / 128U);
@ -599,8 +616,17 @@ void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLev
nxdnDev = uint16_t((ADF7021_DEV_NXDN * uint16_t(nxdnTXLevel)) / 128U); nxdnDev = uint16_t((ADF7021_DEV_NXDN * uint16_t(nxdnTXLevel)) / 128U);
} }
/* Sets the RF adjustment parameters. */ /// <summary>
/// Sets the RF adjustment parameters.
/// </summary>
/// <param name="dmrDevAdj"></param>
/// <param name="p25DevAdj"></param>
/// <param name="dmrDiscBWAdj"></param>
/// <param name="p25DiscBWAdj"></param>
/// <param name="nxdnDiscBWAdj"></param>
/// <param name="dmrPostBWAdj"></param>
/// <param name="p25PostBWAdj"></param>
/// <param name="nxdnPostBWAdj"></param>
void IO::setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWADJ) void IO::setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWADJ)
{ {
m_dmrDiscBWAdj = dmrDiscBWAdj; m_dmrDiscBWAdj = dmrDiscBWAdj;
@ -610,12 +636,17 @@ void IO::setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBW
m_p25PostBWAdj = p25PostBWAdj; m_p25PostBWAdj = p25PostBWAdj;
m_nxdnPostBWAdj = nxdnPostBWADJ; m_nxdnPostBWAdj = nxdnPostBWADJ;
DEBUG4("IO::setRFAdjust() RF adjustment, discBW", dmrDiscBWAdj, p25DiscBWAdj, nxdnDiscBWAdj); DEBUG4("IO::setRFAdjust(): setting RF adjustment, discBW", dmrDiscBWAdj, p25DiscBWAdj, nxdnDiscBWAdj);
DEBUG4("IO::setRFAdjust() RF adjustment, postBW", dmrPostBWAdj, p25PostBWAdj, nxdnPostBWADJ); DEBUG4("IO::setRFAdjust(): setting RF adjustment, postBW", dmrPostBWAdj, p25PostBWAdj, nxdnPostBWADJ);
} }
/* Sets the RF AFC parameters. */ /// <summary>
/// Sets the RF AFC parameters.
/// </summary>
/// <param name="afcEnable"></param>
/// <param name="afcKI"></param>
/// <param name="afcKP"></param>
/// <param name="afcRange"></param>
void IO::setAFCParams(bool afcEnable, uint8_t afcKI, uint8_t afcKP, uint8_t afcRange) void IO::setAFCParams(bool afcEnable, uint8_t afcKI, uint8_t afcKP, uint8_t afcRange)
{ {
m_afcEnable = afcEnable; m_afcEnable = afcEnable;
@ -623,11 +654,12 @@ void IO::setAFCParams(bool afcEnable, uint8_t afcKI, uint8_t afcKP, uint8_t afcR
m_afcKP = afcKP; m_afcKP = afcKP;
m_afcRange = afcRange; m_afcRange = afcRange;
DEBUG5("IO::setAFCParams() AFC params", afcEnable, afcKI, afcKP, afcRange); DEBUG5("IO::setAFCParams(): setting AFC params", afcEnable, afcKI, afcKP, afcRange);
} }
/* */ /// <summary>
///
/// </summary>
void IO::updateCal(DVM_STATE modemState) void IO::updateCal(DVM_STATE modemState)
{ {
uint32_t ADF7021_REG2; uint32_t ADF7021_REG2;
@ -641,7 +673,7 @@ void IO::updateCal(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG1; AD7021_CONTROL = ADF7021_REG1;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
// configure ADF Tx/Rx // configure ADF Tx/RX
configureTxRx(modemState); configureTxRx(modemState);
/* /*
@ -680,7 +712,7 @@ void IO::updateCal(DVM_STATE modemState)
AD7021_CONTROL = ADF7021_REG2; AD7021_CONTROL = ADF7021_REG2;
AD7021_1_IOCTL(); AD7021_1_IOCTL();
DEBUG2("IO::updateCal() ADF calibration; modemState", modemState); DEBUG2("IO::updateCal(): updating ADF calibration; modemState", modemState);
if (m_tx) if (m_tx)
setTX(); setTX();
@ -688,8 +720,10 @@ void IO::updateCal(DVM_STATE modemState)
setRX(); setRX();
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint16_t IO::readRSSI() uint16_t IO::readRSSI()
{ {
uint32_t AD7021_RB; uint32_t AD7021_RB;
@ -778,8 +812,9 @@ uint16_t IO::readRSSI()
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* */ /// <summary>
///
/// </summary>
void IO::configureBand() void IO::configureBand()
{ {
/* /*
@ -846,11 +881,13 @@ void IO::configureBand()
else else
f_div = 1U; f_div = 1U;
DEBUG3("IO::configureBand() ADF freq band; reg1/f_div", ADF7021_REG1, f_div); DEBUG3("IO::configureBand(): configuring ADF freq band; reg1/f_div", ADF7021_REG1, f_div);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="modemState"></param>
void IO::configureTxRx(DVM_STATE modemState) void IO::configureTxRx(DVM_STATE modemState)
{ {
uint16_t dmrDiscBW = ADF7021_DISC_BW_DMR, dmrPostBW = ADF7021_POST_BW_DMR; uint16_t dmrDiscBW = ADF7021_DISC_BW_DMR, dmrPostBW = ADF7021_POST_BW_DMR;
@ -858,61 +895,49 @@ void IO::configureTxRx(DVM_STATE modemState)
uint16_t nxdnDiscBW = ADF7021_DISC_BW_NXDN, nxdnPostBW = ADF7021_POST_BW_NXDN; uint16_t nxdnDiscBW = ADF7021_DISC_BW_NXDN, nxdnPostBW = ADF7021_POST_BW_NXDN;
// configure DMR discriminator and post demodulator BW // configure DMR discriminator and post demodulator BW
if (m_dmrDiscBWAdj != 0) { if (dmrDiscBW + m_dmrDiscBWAdj < 0U)
if (dmrDiscBW + m_dmrDiscBWAdj < 0)
dmrDiscBW = 0U; dmrDiscBW = 0U;
else else
dmrDiscBW = ADF7021_DISC_BW_DMR + m_dmrDiscBWAdj; dmrDiscBW = ADF7021_DISC_BW_DMR + m_dmrDiscBWAdj;
if (dmrDiscBW > ADF7021_DISC_BW_MAX) if (dmrDiscBW > ADF7021_DISC_BW_MAX)
dmrDiscBW = ADF7021_DISC_BW_MAX; dmrDiscBW = ADF7021_DISC_BW_MAX;
}
if (m_dmrPostBWAdj != 0) {
if (dmrPostBW + m_dmrPostBWAdj < 0) if (dmrPostBW + m_dmrPostBWAdj < 0)
dmrPostBW = 0U; dmrPostBW = 0U;
else else
dmrPostBW = ADF7021_POST_BW_DMR + m_dmrPostBWAdj; dmrPostBW = ADF7021_POST_BW_DMR + m_dmrPostBWAdj;
if (dmrPostBW > ADF7021_POST_BW_MAX) if (dmrPostBW > ADF7021_POST_BW_MAX)
dmrPostBW = ADF7021_POST_BW_MAX; dmrPostBW = ADF7021_POST_BW_MAX;
}
// configure P25 discriminator and post demodulator BW // configure P25 discriminator and post demodulator BW
if (m_p25DiscBWAdj != 0) { if (p25DiscBW + m_p25DiscBWAdj < 0U)
if (p25DiscBW + m_p25DiscBWAdj < 0)
p25DiscBW = 0U; p25DiscBW = 0U;
else else
p25DiscBW = ADF7021_DISC_BW_P25 + m_p25DiscBWAdj; p25DiscBW = ADF7021_DISC_BW_P25 + m_p25DiscBWAdj;
if (p25DiscBW > ADF7021_DISC_BW_MAX) if (p25DiscBW > ADF7021_DISC_BW_MAX)
p25DiscBW = ADF7021_DISC_BW_MAX; p25DiscBW = ADF7021_DISC_BW_MAX;
}
if (m_p25PostBWAdj != 0) {
if (p25PostBW + m_p25PostBWAdj < 0) if (p25PostBW + m_p25PostBWAdj < 0)
p25PostBW = 0U; p25PostBW = 0U;
else else
p25PostBW = ADF7021_POST_BW_P25 + m_p25PostBWAdj; p25PostBW = ADF7021_POST_BW_P25 + m_p25PostBWAdj;
if (p25PostBW > ADF7021_POST_BW_MAX) if (p25PostBW > ADF7021_POST_BW_MAX)
p25PostBW = ADF7021_POST_BW_MAX; p25PostBW = ADF7021_POST_BW_MAX;
}
// configure NXDN discriminator and post demodulator BW // configure NXDN discriminator and post demodulator BW
if (m_nxdnDiscBWAdj != 0) { if (nxdnDiscBW + m_nxdnDiscBWAdj < 0U)
if (nxdnDiscBW + m_nxdnDiscBWAdj < 0)
nxdnDiscBW = 0U; nxdnDiscBW = 0U;
else else
nxdnDiscBW = ADF7021_DISC_BW_NXDN + m_nxdnDiscBWAdj; nxdnDiscBW = ADF7021_DISC_BW_NXDN + m_nxdnDiscBWAdj;
if (nxdnDiscBW > ADF7021_DISC_BW_MAX) if (nxdnDiscBW > ADF7021_DISC_BW_MAX)
nxdnDiscBW = ADF7021_DISC_BW_MAX; nxdnDiscBW = ADF7021_DISC_BW_MAX;
}
if (m_nxdnPostBWAdj != 0) {
if (nxdnPostBW + m_nxdnPostBWAdj < 0) if (nxdnPostBW + m_nxdnPostBWAdj < 0)
nxdnPostBW = 0U; nxdnPostBW = 0U;
else else
nxdnPostBW = ADF7021_POST_BW_NXDN + m_nxdnPostBWAdj; nxdnPostBW = ADF7021_POST_BW_NXDN + m_nxdnPostBWAdj;
if (nxdnPostBW > ADF7021_POST_BW_MAX) if (nxdnPostBW > ADF7021_POST_BW_MAX)
nxdnPostBW = ADF7021_POST_BW_MAX; nxdnPostBW = ADF7021_POST_BW_MAX;
}
/* /*
** Configure the remaining registers based on modem state. ** Configure the remaining registers based on modem state.
@ -982,7 +1007,7 @@ void IO::configureTxRx(DVM_STATE modemState)
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
ADF7021_REG13 = (uint32_t)ADF7021_REG13_ADDR; // Register Address 13 ADF7021_REG13 = (uint32_t)0b1101 << 0; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DMR << 4; // Slicer Threshold ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DMR << 4; // Slicer Threshold
/* /*
@ -1056,12 +1081,12 @@ void IO::configureTxRx(DVM_STATE modemState)
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_INV_CLKDAT << 8; // Clock/Data Inversion ADF7021_REG4 |= (uint32_t)ADF7021_REG4_INV_CLKDAT << 8; // Clock/Data Inversion
ADF7021_REG4 |= (uint32_t)(dmrDiscBW & 0x3FFU) << 10; // Discriminator BW ADF7021_REG4 |= (uint32_t)(dmrDiscBW & 0x3FFU) << 10; // Discriminator BW
ADF7021_REG4 |= (uint32_t)(dmrPostBW & 0xFFFU) << 20; // Post Demod BW ADF7021_REG4 |= (uint32_t)(dmrPostBW & 0xFFFU) << 20; // Post Demod BW
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_IF_25K << 30; // IF Filter ADF7021_REG4 |= (uint32_t)ADF7021_REG4_IF_125K << 30; // IF Filter
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
ADF7021_REG13 = (uint32_t)ADF7021_REG13_ADDR; // Register Address 13 ADF7021_REG13 = (uint32_t)0b1101 << 0; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DMR << 4; // Slicer Threshold ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DMR << 4; // Slicer Threshold
/* /*
@ -1072,7 +1097,7 @@ void IO::configureTxRx(DVM_STATE modemState)
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_PA_DEF << 7; // PA Enable & PA Bias ADF7021_REG2 |= (uint32_t)ADF7021_REG2_PA_DEF << 7; // PA Enable & PA Bias
ADF7021_REG2 |= (uint32_t)(m_rfPower & 0x3FU) << 13; // PA Level (0 - Off, 63 - 13 dBm) ADF7021_REG2 |= (uint32_t)(m_rfPower & 0x3FU) << 13; // PA Level (0 - Off, 63 - 13 dBm)
ADF7021_REG2 |= (uint32_t)(dmrDev / div2) << 19; // Freq. Deviation ADF7021_REG2 |= (uint32_t)(dmrDev / div2) << 19; // Freq. Deviation
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_INV_DATA << 28; // Data Inversion ADF7021_REG2 |= (uint32_t)ADF7021_REG2_INV_CLKDAT << 28; // Clock/Data Inversion
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_RC_5 << 30; // R-Cosine Alpha ADF7021_REG2 |= (uint32_t)ADF7021_REG2_RC_5 << 30; // R-Cosine Alpha
} }
break; break;
@ -1135,12 +1160,12 @@ void IO::configureTxRx(DVM_STATE modemState)
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_INV_CLKDAT << 8; // Clock/Data Inversion ADF7021_REG4 |= (uint32_t)ADF7021_REG4_INV_CLKDAT << 8; // Clock/Data Inversion
ADF7021_REG4 |= (uint32_t)(p25DiscBW & 0x3FFU) << 10; // Discriminator BW ADF7021_REG4 |= (uint32_t)(p25DiscBW & 0x3FFU) << 10; // Discriminator BW
ADF7021_REG4 |= (uint32_t)(p25PostBW & 0xFFFU) << 20; // Post Demod BW ADF7021_REG4 |= (uint32_t)(p25PostBW & 0xFFFU) << 20; // Post Demod BW
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_IF_25K << 30; // IF Filter ADF7021_REG4 |= (uint32_t)ADF7021_REG4_IF_125K << 30; // IF Filter
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
ADF7021_REG13 = (uint32_t)ADF7021_REG13_ADDR; // Register Address 13 ADF7021_REG13 = (uint32_t)ADF70210_REG13_ADDR; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_P25 << 4; // Slicer Threshold ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_P25 << 4; // Slicer Threshold
/* /*
@ -1231,7 +1256,7 @@ void IO::configureTxRx(DVM_STATE modemState)
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
ADF7021_REG13 = (uint32_t)ADF7021_REG13_ADDR; // Register Address 13 ADF7021_REG13 = (uint32_t)ADF70210_REG13_ADDR; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_NXDN << 4; // Slicer Threshold ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_NXDN << 4; // Slicer Threshold
/* /*
@ -1309,7 +1334,7 @@ void IO::configureTxRx(DVM_STATE modemState)
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
ADF7021_REG13 = (uint32_t)ADF7021_REG13_ADDR; // Register Address 13 ADF7021_REG13 = (uint32_t)ADF70210_REG13_ADDR; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DEFAULT << 4; // Slicer Threshold ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_DEFAULT << 4; // Slicer Threshold
/* /*
@ -1326,14 +1351,15 @@ void IO::configureTxRx(DVM_STATE modemState)
break; break;
} }
DEBUG5("IO::configureTxRx() ADF Tx/Rx values; dmrDiscBW/dmrPostBW/p25DiscBW/p25PostBW", dmrDiscBW, dmrPostBW, p25DiscBW, p25PostBW); DEBUG5("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrDiscBW/dmrPostBW/p25DiscBW/p25PostBW", dmrDiscBW, dmrPostBW, p25DiscBW, p25PostBW);
DEBUG3("IO::configureTxRx() ADF Tx/Rx values; nxdnDiscBW/nxdnPostBW", nxdnDiscBW, nxdnPostBW); DEBUG3("IO::configureTxRx(): configuring ADF Tx/Rx values; nxdnDiscBW/nxdnPostBW", nxdnDiscBW, nxdnPostBW);
DEBUG5("IO::configureTxRx() ADF Tx/Rx values; dmrSymDev/p25SymDev/nxdnSymDev/rfPower", (uint16_t)((ADF7021_PFD * dmrDev) / (f_div * 65536)), DEBUG5("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrSymDev/p25SymDev/nxdnSymDev/rfPower", (uint16_t)((ADF7021_PFD * dmrDev) / (f_div * 65536)),
(uint16_t)((ADF7021_PFD * p25Dev) / (f_div * 65536)), (uint16_t)((ADF7021_PFD * nxdnDev) / (f_div * 65536)), m_rfPower); (uint16_t)((ADF7021_PFD * p25Dev) / (f_div * 65536)), (uint16_t)((ADF7021_PFD * nxdnDev) / (f_div * 65536)), m_rfPower);
} }
/* */ /// <summary>
///
/// </summary>
void IO::setTX() void IO::setTX()
{ {
// PTT pin on (doing it earlier helps to measure timing impact) // PTT pin on (doing it earlier helps to measure timing impact)
@ -1352,8 +1378,10 @@ void IO::setTX()
while(CLK()); while(CLK());
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="doSle"></param>
void IO::setRX(bool doSle) void IO::setRX(bool doSle)
{ {
// PTT pin off (doing it earlier helps to measure timing impact) // PTT pin off (doing it earlier helps to measure timing impact)

@ -1,20 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2020 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Jim McLaughlin, KI6ZUM
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU
* Copyright (C) 2017 Danilo, DB4PLE
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file ADF7021.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
*/ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* Some of the code is based on work of Guus Van Dooren PE1PLM:
* https://github.com/ki6zum/gmsk-dstar/blob/master/firmware/dvmega/dvmega.ino
*
* 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(__ADF7021_H__) #if !defined(__ADF7021_H__)
#define __ADF7021_H__ #define __ADF7021_H__
@ -24,11 +43,6 @@
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @addtogroup hotspot_fw
* @{
*/
#define LOW 0 #define LOW 0
#define HIGH 1 #define HIGH 1
@ -296,7 +310,7 @@
/* /*
** 3FSK/4FSK Demod (Register 13) ** 3FSK/4FSK Demod (Register 13)
*/ */
#define ADF7021_REG13_ADDR 0b1101 #define ADF70210_REG13_ADDR 0b1101
// Slicer threshold for 4FSK demodulator // Slicer threshold for 4FSK demodulator
#define ADF7021_SLICER_TH_DEFAULT 0U #define ADF7021_SLICER_TH_DEFAULT 0U
@ -317,5 +331,4 @@
#endif // ADF7021_N_VER #endif // ADF7021_N_VER
#endif // ENABLE_ADF7021 #endif // ENABLE_ADF7021
/** @} */
#endif // __ADF7021_H__ #endif // __ADF7021_H__

@ -0,0 +1,11 @@
# Digital Voice Modem Firmware (Hotspot)
## Project Technical Leads
- Bryan Biedenkapp (https://github.com/gatekeep)
## Developers
- Natalie Moore (https://github.com/jelimoore)
## Special thanks to
- Jonathan Naylor G4KLX (https://github.com/g4klx) and the MMDVM authors.
- Andy Uribe CA6JAU (https://github.com/juribeparada)

@ -1,21 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* * This library is free software; you can redistribute it and/or
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * modify it under the terms of the GNU Library General Public
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin, KI6ZUM * 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 "BitBuffer.h" #include "BitBuffer.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the BitBuffer class. */ /// <summary>
/// Initializes a new instance of the BitBuffer class.
/// </summary>
BitBuffer::BitBuffer(uint16_t length) : BitBuffer::BitBuffer(uint16_t length) :
m_length(length), m_length(length),
m_bits(NULL), m_bits(NULL),
@ -29,8 +52,10 @@ BitBuffer::BitBuffer(uint16_t length) :
m_control = new uint8_t[length / 8U]; m_control = new uint8_t[length / 8U];
} }
/* Helper to get how much space the ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t BitBuffer::getSpace() const uint16_t BitBuffer::getSpace() const
{ {
uint16_t n = 0U; uint16_t n = 0U;
@ -48,8 +73,10 @@ uint16_t BitBuffer::getSpace() const
return n; return n;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint16_t BitBuffer::getData() const uint16_t BitBuffer::getData() const
{ {
if (m_tail == m_head) if (m_tail == m_head)
@ -60,8 +87,11 @@ uint16_t BitBuffer::getData() const
return m_length - m_tail + m_head; return m_length - m_tail + m_head;
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool BitBuffer::put(uint8_t bit, uint8_t control) bool BitBuffer::put(uint8_t bit, uint8_t control)
{ {
if (m_full) { if (m_full) {
@ -82,8 +112,10 @@ bool BitBuffer::put(uint8_t bit, uint8_t control)
return true; return true;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool BitBuffer::get(uint8_t& bit, uint8_t& control) bool BitBuffer::get(uint8_t& bit, uint8_t& control)
{ {
if (m_head == m_tail && !m_full) if (m_head == m_tail && !m_full)
@ -101,8 +133,10 @@ bool BitBuffer::get(uint8_t& bit, uint8_t& control)
return true; return true;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool BitBuffer::hasOverflowed() bool BitBuffer::hasOverflowed()
{ {
bool overflow = m_overflow; bool overflow = m_overflow;

@ -1,19 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin, KI6ZUM
*
*/
/** /**
* @file BitBuffer.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file BitBuffer.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
*
* 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(__BIT_RB_H__) #if !defined(__BIT_RB_H__)
#define __BIT_RB_H__ #define __BIT_RB_H__
@ -28,52 +44,27 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements a circular buffer for bit data.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements a circular ring buffer for bit data.
* @ingroup hotspot_fw
*/
class DSP_FW_API BitBuffer { class DSP_FW_API BitBuffer {
public: public:
/** /// <summary>Initializes a new instance of the BitBuffer class.</summary>
* @brief Initializes a new instance of the BitBuffer class.
* @param length Length of buffer.
*/
BitBuffer(uint16_t length); BitBuffer(uint16_t length);
/** /// <summary>Helper to get how much space the ring buffer has for samples.</summary>
* @brief Helper to get how much space the ring buffer has for samples.
* @returns uint16_t Amount of space remaining for data.
*/
uint16_t getSpace() const; uint16_t getSpace() const;
/** /// <summary></summary>
* @brief
* @returns uint16_t
*/
uint16_t getData() const; uint16_t getData() const;
/** /// <summary></summary>
* @brief
* @param bit
* @param control
* @returns bool
*/
bool put(uint8_t bit, uint8_t control); bool put(uint8_t bit, uint8_t control);
/** /// <summary></summary>
* @brief
* @param[out] bit
* @param[out] control
* @returns bool
*/
bool get(uint8_t& bit, uint8_t& control); bool get(uint8_t& bit, uint8_t& control);
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool hasOverflowed(); bool hasOverflowed();
private: private:

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

@ -1,14 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Colin Durbridge G4EML
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2017 by Andy Uribe CA6JAU
* *
* Copyright (C) 2009-2017 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016 Colin Durbridge, G4EML * it under the terms of the GNU General Public License as published by
* Copyright (C) 2017 Andy Uribe, CA6JAU * 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 "Globals.h" #include "Globals.h"
#include "CWIdTX.h" #include "CWIdTX.h"
@ -81,8 +101,9 @@ const struct {
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the CWIdTX class. */ /// <summary>
/// Initializes a new instance of the CWIdTX class.
/// </summary>
CWIdTX::CWIdTX() : CWIdTX::CWIdTX() :
m_poBuffer(), m_poBuffer(),
m_poLen(0U), m_poLen(0U),
@ -92,8 +113,9 @@ CWIdTX::CWIdTX() :
/* stub */ /* stub */
} }
/* Process local buffer and transmit on the air interface. */ /// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void CWIdTX::process() void CWIdTX::process()
{ {
if (m_poLen == 0U) if (m_poLen == 0U)
@ -124,8 +146,12 @@ void CWIdTX::process()
} }
} }
/* Write CW ID data to the local buffer. */ /// <summary>
/// Write CW ID data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CWIdTX::write(const uint8_t* data, uint8_t length) uint8_t CWIdTX::write(const uint8_t* data, uint8_t length)
{ {
::memset(m_poBuffer, 0x00U, 300U * sizeof(uint8_t)); ::memset(m_poBuffer, 0x00U, 300U * sizeof(uint8_t));
@ -161,13 +187,14 @@ uint8_t CWIdTX::write(const uint8_t* data, uint8_t length)
m_poLen += 5U; m_poLen += 5U;
DEBUG2("CWIdTx::write() message length", m_poLen); DEBUG2("CWIdTx: write(): message created with length", m_poLen);
return RSN_OK; return RSN_OK;
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void CWIdTX::reset() void CWIdTX::reset()
{ {
m_poLen = 0U; m_poLen = 0U;

@ -1,20 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2009-2015 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Colin Durbridge, G4EML
* Copyright (C) 2017 Andy Uribe, CA6JAU
*
*/
/** /**
* @file CWIdTX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file CWIdTX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2017 by Andy Uribe CA6JAU
*
* 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(__CWID_TX_H__) #if !defined(__CWID_TX_H__)
#define __CWID_TX_H__ #define __CWID_TX_H__
@ -22,35 +36,21 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements logic to transmit a CW ID.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements logic to transmit a CW ID.
* @ingroup hotspot_fw
*/
class DSP_FW_API CWIdTX { class DSP_FW_API CWIdTX {
public: public:
/** /// <summary>Initializes a new instance of the CWIdTX class.</summary>
* @brief Initializes a new instance of the CWIdTX class.
*/
CWIdTX(); CWIdTX();
/** /// <summary>Process local buffer and transmit on the air interface.</summary>
* @brief Process local buffer and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write CW ID data to the local buffer.</summary>
* @brief Write CW ID data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t write(const uint8_t* data, uint8_t length); uint8_t write(const uint8_t* data, uint8_t length);
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
private: private:

@ -1,13 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016 Jonathan Naylor, G4KLX * it under the terms of the GNU General Public License as published by
* Copyright (C) 2018 Andy Uribe, CA6JAU * 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 "Globals.h" #include "Globals.h"
#include "CalRSSI.h" #include "CalRSSI.h"
#include "Utils.h" #include "Utils.h"
@ -16,8 +36,9 @@
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the CalRSSI class. */ /// <summary>
/// Initializes a new instance of the CalRSSI class.
/// </summary>
CalRSSI::CalRSSI() : CalRSSI::CalRSSI() :
m_count(0U), m_count(0U),
m_navg(0U), m_navg(0U),
@ -28,8 +49,9 @@ CalRSSI::CalRSSI() :
/* stub */ /* stub */
} }
/* Sample RSSI values from the air interface. */ /// <summary>
/// Sample RSSI values from the air interface.
/// </summary>
void CalRSSI::process() void CalRSSI::process()
{ {
m_count++; m_count++;

@ -1,19 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2016 Jonathan Naylor, G4KLX
* Copyright (C) 2018 Andy Uribe, CA6JAU
*
*/
/** /**
* @file CalRSSI.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file CalRSSI.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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(__CAL_RSSI_H__) #if !defined(__CAL_RSSI_H__)
#define __CAL_RSSI_H__ #define __CAL_RSSI_H__
@ -26,14 +40,10 @@
class DSP_FW_API CalRSSI { class DSP_FW_API CalRSSI {
public: public:
/** /// <summary>Initializes a new instance of the CalRSSI class.</summary>
* @brief Initializes a new instance of the CalRSSI class.
*/
CalRSSI(); CalRSSI();
/** /// <summary>Sample RSSI values from the air interface.</summary>
* @brief Sample RSSI values from the air interface.
*/
void process(); void process();
private: private:

@ -1,18 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2018,2019,2020 Andy Uribe, CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file Defines.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
*/ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DEFINES_H__) #if !defined(__DEFINES_H__)
#define __DEFINES_H__ #define __DEFINES_H__
@ -76,6 +92,15 @@ typedef unsigned long long ulong64_t;
#define DSP_FW_API #define DSP_FW_API
// Allow the DMR protocol
#define ENABLE_DMR
// Allow the P25 protocol
#define ENABLE_P25
// Allow the NXDN protocol
#define ENABLE_NXDN
// Enable ADF7021 support // Enable ADF7021 support
#define ENABLE_ADF7021 #define ENABLE_ADF7021
@ -98,14 +123,29 @@ typedef unsigned long long ulong64_t;
#define FORCE_UHF_INTERAL_L #define FORCE_UHF_INTERAL_L
// Alternate P25 Deviation Levels // Alternate P25 Deviation Levels
// #define P25_ALTERNATE_DEV_LEVEL //#define P25_ALTERNATE_DEV_LEVEL
// Pass RSSI information to the host // Pass RSSI information to the host
// #define SEND_RSSI_DATA // #define SEND_RSSI_DATA
// Enable for RPi 3B+, USB mode
#define LONG_USB_RESET
#if defined(ENABLE_DMR)
#define DESCR_DMR "DMR, " #define DESCR_DMR "DMR, "
#else
#define DESCR_DMR ""
#endif
#if defined(ENABLE_P25)
#define DESCR_P25 "P25, " #define DESCR_P25 "P25, "
#else
#define DESCR_P25 ""
#endif
#if defined(ENABLE_NXDN)
#define DESCR_NXDN "NXDN, " #define DESCR_NXDN "NXDN, "
#else
#define DESCR_NXDN ""
#endif
#if defined(SEND_RSSI_DATA) #if defined(SEND_RSSI_DATA)
#define DESCR_RSSI "RSSI, " #define DESCR_RSSI "RSSI, "
@ -157,4 +197,8 @@ const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x0
#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 _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]) >> (7 - ((i) & 7))) #define _READ_BIT(p, i) ((p[(i) >> 3] & BIT_MASK_TABLE[(i) & 7]) >> (7 - ((i) & 7)))
#if !defined(ENABLE_DMR) && !defined(ENABLE_P25) && !defined(ENABLE_NXDN)
#error "No protocol support compiled in? Must enable at least one: ENABLE_DMR, ENABLE_P25 and/or ENABLE_NXDN."
#endif
#endif // __DEFINES_H__ #endif // __DEFINES_H__

@ -1,17 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Mathis Schmieder DB9MAT
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2016 by Colin Durbridge G4EML
* * Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* Copyright (C) 2015,2016,2020 Jonathan Naylor, G4KLX * Copyright (C) 2019 by Florian Wolters DF2ET
* Copyright (C) 2016 Mathis Schmieder, DB9MAT *
* Copyright (C) 2016 Colin Durbridge, G4EML * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017,2018,2019 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2019 Florian Wolters, DF2ET * the Free Software Foundation; either version 2 of the License, or
* Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * (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 "Globals.h" #include "Globals.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -23,47 +42,70 @@ DVM_STATE m_modemState = STATE_IDLE;
bool m_cwIdState = false; bool m_cwIdState = false;
uint8_t m_cwIdTXLevel = 30; uint8_t m_cwIdTXLevel = 30;
#ifdef ENABLE_DMR
bool m_dmrEnable = true; bool m_dmrEnable = true;
#else
bool m_dmrEnable = false;
#endif
#ifdef ENABLE_P25
bool m_p25Enable = true; bool m_p25Enable = true;
#else
bool m_p25Enable = false;
#endif
#ifdef ENABLE_NXDN
bool m_nxdnEnable = true; bool m_nxdnEnable = true;
#else
bool m_nxdnEnable = false;
#endif
bool m_duplex = false; bool m_duplex = false;
bool m_forceDMO = false;
bool m_tx = false; bool m_tx = false;
bool m_dcd = false; bool m_dcd = false;
uint8_t m_control; uint8_t m_control;
/* DMR BS */ /** DMR BS */
#if defined(ENABLE_DMR)
#if defined(DUPLEX) #if defined(DUPLEX)
dmr::DMRIdleRX dmrIdleRX; dmr::DMRIdleRX dmrIdleRX;
dmr::DMRRX dmrRX; dmr::DMRRX dmrRX;
#endif #endif
dmr::DMRTX dmrTX; dmr::DMRTX dmrTX;
/* DMR MS-DMO */ /** DMR MS-DMO */
dmr::DMRDMORX dmrDMORX; dmr::DMRDMORX dmrDMORX;
dmr::DMRDMOTX dmrDMOTX; dmr::DMRDMOTX dmrDMOTX;
#endif
/* P25 */ /** P25 */
#if defined(ENABLE_P25)
p25::P25RX p25RX; p25::P25RX p25RX;
p25::P25TX p25TX; p25::P25TX p25TX;
#endif
/* NXDN */ /** NXDN */
#if defined(ENABLE_NXDN)
nxdn::NXDNRX nxdnRX; nxdn::NXDNRX nxdnRX;
nxdn::NXDNTX nxdnTX; nxdn::NXDNTX nxdnTX;
#endif
/* Calibration */ /** Calibration */
#if defined(ENABLE_DMR)
dmr::CalDMR calDMR; dmr::CalDMR calDMR;
#endif
#if defined(ENABLE_P25)
p25::CalP25 calP25; p25::CalP25 calP25;
#endif
#if defined(ENABLE_NXDN)
nxdn::CalNXDN calNXDN; nxdn::CalNXDN calNXDN;
#endif
CalRSSI calRSSI; CalRSSI calRSSI;
/* CW */ /** CW */
CWIdTX cwIdTX; CWIdTX cwIdTX;
/* RS232 and Air Interface I/O */ /** RS232 and Air Interface I/O */
SerialPort serial; SerialPort serial;
IO io; IO io;
@ -73,8 +115,6 @@ IO io;
void setup() void setup()
{ {
io.init();
serial.start(); serial.start();
} }
@ -85,6 +125,7 @@ void loop()
io.process(); io.process();
// The following is for transmitting // The following is for transmitting
#if defined(ENABLE_DMR)
if (m_dmrEnable && m_modemState == STATE_DMR) { if (m_dmrEnable && m_modemState == STATE_DMR) {
#if defined(DUPLEX) #if defined(DUPLEX)
if (m_duplex) if (m_duplex)
@ -93,25 +134,36 @@ void loop()
dmrDMOTX.process(); dmrDMOTX.process();
#else #else
dmrDMOTX.process(); dmrDMOTX.process();
#endif #endif // defined(DUPLEX)
} }
#endif // defined(ENABLE_DMR)
#if defined(ENABLE_P25)
if (m_p25Enable && m_modemState == STATE_P25) if (m_p25Enable && m_modemState == STATE_P25)
p25TX.process(); p25TX.process();
#endif // defined(ENABLE_P25)
#if defined(ENABLE_NXDN)
if (m_nxdnEnable && m_modemState == STATE_NXDN) if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process(); nxdnTX.process();
#endif // defined(ENABLE_NXDN)
#if defined(ENABLE_DMR)
if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K || if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K ||
m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL || m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL ||
m_modemState == STATE_INT_CAL) m_modemState == STATE_INT_CAL)
calDMR.process(); calDMR.process();
#endif // defined(ENABLE_DMR)
#if defined(ENABLE_P25)
if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_CAL) if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_CAL)
calP25.process(); calP25.process();
#endif // defined(ENABLE_P25)
#if defined(ENABLE_NXDN)
if (m_modemState == STATE_NXDN_CAL) if (m_modemState == STATE_NXDN_CAL)
calNXDN.process(); calNXDN.process();
#endif // defined(ENABLE_NXDN)
if (m_modemState == STATE_RSSI_CAL) if (m_modemState == STATE_RSSI_CAL)
calRSSI.process(); calRSSI.process();
@ -123,68 +175,9 @@ void loop()
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Firmware Entry Point // Firmware Entry Point
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#include <stm32f10x_flash.h>
#define STM32_CNF_PAGE_ADDR (uint32_t)0x0800FC00
#define STM32_CNF_PAGE ((uint32_t *)0x0800FC00)
#define STM32_CNF_PAGE_24 24U
void jumpToBootLoader()
{
// Disable RCC, set it to default (after reset) settings Internal clock, no PLL, etc.
RCC_DeInit();
USART_DeInit(USART1);
USART_DeInit(UART5);
// Disable Systick timer
SysTick->CTRL = 0;
SysTick->LOAD = 0;
SysTick->VAL = 0;
// Clear Interrupt Enable Register & Interrupt Pending Register
for (uint8_t i = 0; i < sizeof(NVIC->ICER) / sizeof(NVIC->ICER[0]); i++) {
NVIC->ICER[i] = 0xFFFFFFFF;
NVIC->ICPR[i] = 0xFFFFFFFF;
}
#if defined(STM32F10X_MD)
volatile uint32_t addr = 0x1FFFF000;
#endif
// Update the NVIC's vector
SCB->VTOR = addr;
void (*SysMemBootJump)(void);
SysMemBootJump = (void (*)(void))(*((uint32_t *)(addr + 4)));
__ASM volatile ("MSR msp, %0" : : "r" (*(uint32_t *)addr) : "sp"); // __set_MSP
SysMemBootJump();
}
int main() int main()
{ {
// does the configuration page contain the request bootloader flag?
if ((STM32_CNF_PAGE[STM32_CNF_PAGE_24] != 0xFFFFFFFFU) && (STM32_CNF_PAGE[STM32_CNF_PAGE_24] != 0x00U)) {
uint8_t bootloadMode = (STM32_CNF_PAGE[STM32_CNF_PAGE_24] >> 8) & 0xFFU;
if ((bootloadMode & 0x20U) == 0x20U) {
// we unfortunately need to discard the configuration area entirely for bootloader mode...
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
#if defined(STM32F4XX) || defined(STM32F7XX)
if (FLASH_EraseSector(STM32_CNF_SECTOR, VoltageRange_3) != FLASH_COMPLETE) {
FLASH_Lock();
return RSN_FAILED_ERASE_FLASH;
}
#elif defined(STM32F10X_MD)
if (FLASH_ErasePage(STM32_CNF_PAGE_ADDR) != FLASH_COMPLETE) {
FLASH_Lock();
return RSN_FAILED_ERASE_FLASH;
}
#endif
jumpToBootLoader();
}
}
setup(); setup();
for (;;) for (;;)

@ -1,24 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2020 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017,2018,2019 Andy Uribe, CA6JAU
* Copyright (C) 2019 Florian Wolters, DF2ET
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @defgroup hotspot_fw Hotspot Firmware * Digital Voice Modem - DSP Firmware (Hotspot)
* @brief Digital Voice Modem - Hotspot Firmware * GPLv2 Open Source. Use is subject to license terms.
* @details Firmware that is used for all-in-one hotspots. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
* * @package DVM / DSP Firmware (Hotspot)
* @file Globals.h *
* @ingroup hotspot_fw */
*/ //
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* Copyright (C) 2019 by Florian Wolters DF2ET
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__GLOBALS_H__) #if !defined(__GLOBALS_H__)
#define __GLOBALS_H__ #define __GLOBALS_H__
@ -61,8 +72,8 @@ const uint8_t MARK_SLOT1 = 0x08U;
const uint8_t MARK_SLOT2 = 0x04U; const uint8_t MARK_SLOT2 = 0x04U;
const uint8_t MARK_NONE = 0x00U; const uint8_t MARK_NONE = 0x00U;
const uint8_t CONTROL_SLOT1 = 0x01U; const uint8_t CONTROL_SLOT1 = 0x00U;
const uint8_t CONTROL_SLOT2 = 0x00U; const uint8_t CONTROL_SLOT2 = 0x01U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macros // Macros
@ -89,7 +100,6 @@ extern bool m_p25Enable;
extern bool m_nxdnEnable; extern bool m_nxdnEnable;
extern bool m_duplex; extern bool m_duplex;
extern bool m_forceDMO;
extern bool m_tx; extern bool m_tx;
extern bool m_dcd; extern bool m_dcd;
@ -99,32 +109,44 @@ extern uint8_t m_control;
extern SerialPort serial; extern SerialPort serial;
extern IO io; extern IO io;
/* DMR BS */ /** DMR BS */
#if defined(ENABLE_DMR)
#if defined(DUPLEX) #if defined(DUPLEX)
extern dmr::DMRIdleRX dmrIdleRX; extern dmr::DMRIdleRX dmrIdleRX;
extern dmr::DMRRX dmrRX; extern dmr::DMRRX dmrRX;
#endif #endif // defined(DUPLEX)
extern dmr::DMRTX dmrTX; extern dmr::DMRTX dmrTX;
/* DMR MS-DMO */ /** DMR MS-DMO */
extern dmr::DMRDMORX dmrDMORX; extern dmr::DMRDMORX dmrDMORX;
extern dmr::DMRDMOTX dmrDMOTX; extern dmr::DMRDMOTX dmrDMOTX;
#endif // defined(ENABLE_DMR)
/* P25 BS */ /** P25 BS */
#if defined(ENABLE_P25)
extern p25::P25RX p25RX; extern p25::P25RX p25RX;
extern p25::P25TX p25TX; extern p25::P25TX p25TX;
#endif // defined(ENABLE_P25)
/* NXDN BS */ /** NXDN BS */
#if defined(ENABLE_NXDN)
extern nxdn::NXDNRX nxdnRX; extern nxdn::NXDNRX nxdnRX;
extern nxdn::NXDNTX nxdnTX; extern nxdn::NXDNTX nxdnTX;
#endif // defined(ENABLE_NXDN)
/* Calibration */ /** Calibration */
#if defined(ENABLE_DMR)
extern dmr::CalDMR calDMR; extern dmr::CalDMR calDMR;
#endif // defined(ENABLE_DMR)
#if defined(ENABLE_P25)
extern p25::CalP25 calP25; extern p25::CalP25 calP25;
#endif // defined(ENABLE_P25)
#if defined(ENABLE_NXDN)
extern nxdn::CalNXDN calNXDN; extern nxdn::CalNXDN calNXDN;
#endif // defined(ENABLE_NXDN)
extern CalRSSI calRSSI; extern CalRSSI calRSSI;
/* CW */ /** CW */
extern CWIdTX cwIdTX; extern CWIdTX cwIdTX;
#endif // __GLOBALS_H__ #endif // __GLOBALS_H__

187
IO.cpp

@ -1,15 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2017 by Danilo DB4PLE
* * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* Copyright (C) 2015,2016,2020 Jonathan Naylor, G4KLX *
* Copyright (C) 2016,2017,2018,2019,2020 Andy Uribe, CA6JAU * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017 Danilo, DB4PLE * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "ADF7021.h" #include "ADF7021.h"
#include "IO.h" #include "IO.h"
@ -18,8 +38,9 @@
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the IO class. */ /// <summary>
/// Initializes a new instance of the IO class.
/// </summary>
IO::IO(): IO::IO():
m_started(false), m_started(false),
m_rxBuffer(1024U), m_rxBuffer(1024U),
@ -33,13 +54,6 @@ IO::IO():
m_txFrequency(DEFAULT_FREQUENCY), m_txFrequency(DEFAULT_FREQUENCY),
m_rfPower(0U), m_rfPower(0U),
m_gainMode(ADF_GAIN_AUTO) m_gainMode(ADF_GAIN_AUTO)
{
/* stub */
}
/* Initializes the air interface sampler. */
void IO::init()
{ {
initInt(); initInt();
@ -66,8 +80,9 @@ void IO::init()
selfTest(); selfTest();
} }
/* Starts air interface sampler. */ /// <summary>
/// Starts air interface sampler.
/// </summary>
void IO::start() void IO::start()
{ {
if (m_started) if (m_started)
@ -78,8 +93,9 @@ void IO::start()
m_started = true; m_started = true;
} }
/* Process samples from air interface. */ /// <summary>
/// Process samples from air interface.
/// </summary>
void IO::process() void IO::process()
{ {
uint8_t bit; uint8_t bit;
@ -89,13 +105,12 @@ void IO::process()
if (m_started) { if (m_started) {
// Two seconds timeout // Two seconds timeout
if (m_watchdog >= 19200U) { if (m_watchdog >= 19200U) {
if (m_modemState == STATE_DMR || m_modemState == STATE_P25 || m_modemState == STATE_NXDN) { /*
#if defined(DUPLEX) if (m_modemState == STATE_DMR || m_modemState == STATE_P25) {
if (m_modemState == STATE_DMR && m_tx) m_modemState = STATE_IDLE;
dmrTX.setStart(false); setMode(m_modemState);
#endif
} }
*/
m_watchdog = 0U; m_watchdog = 0U;
} }
@ -119,7 +134,7 @@ void IO::process()
if (m_cwIdState) { if (m_cwIdState) {
// check for CW ID end of transmission // check for CW ID end of transmission
m_cwIdState = false; m_cwIdState = false;
DEBUG2("IO::process() setting modem state", m_modemState); DEBUG2("IO::process(): setting modem state", m_modemState);
io.rf1Conf(m_modemState, true); io.rf1Conf(m_modemState, true);
} }
@ -130,9 +145,10 @@ void IO::process()
m_rxBuffer.get(bit, control); m_rxBuffer.get(bit, control);
if (m_modemState == STATE_DMR) { if (m_modemState == STATE_DMR) {
#if defined(ENABLE_DMR)
/** Digital Mobile Radio */ /** Digital Mobile Radio */
#if defined(DUPLEX) #if defined(DUPLEX)
if (m_duplex && !m_forceDMO) { if (m_duplex) {
if (m_tx) if (m_tx)
dmrRX.databit(bit, control); dmrRX.databit(bit, control);
else else
@ -142,21 +158,31 @@ void IO::process()
dmrDMORX.databit(bit); dmrDMORX.databit(bit);
#else #else
dmrDMORX.databit(bit); dmrDMORX.databit(bit);
#endif #endif // defined(DUPLEX)
#endif // defined(ENABLE_DMR)
} }
else if (m_modemState == STATE_P25) { else if (m_modemState == STATE_P25) {
#if defined(ENABLE_P25)
/** Project 25 */ /** Project 25 */
p25RX.databit(bit); p25RX.databit(bit);
#endif // defined(ENABLE_P25)
} }
else if (m_modemState == STATE_NXDN) { else if (m_modemState == STATE_NXDN) {
#if defined(ENABLE_NXDN)
/** Next Generation Digital Narrowband */ /** Next Generation Digital Narrowband */
nxdnRX.databit(bit); nxdnRX.databit(bit);
#endif // defined(ENABLE_NXDN)
} }
} }
} }
/* Write bits to air interface. */ /// <summary>
/// Write bits to air interface.
/// </summary>
/// <param name="mode"></param>
/// <param name="samples"></param>
/// <param name="length"></param>
/// <param name="control"></param>
void IO::write(uint8_t* data, uint16_t length, const uint8_t* control) void IO::write(uint8_t* data, uint16_t length, const uint8_t* control)
{ {
if (!m_started) if (!m_started)
@ -176,15 +202,19 @@ void IO::write(uint8_t* data, uint16_t length, const uint8_t* control)
} }
} }
/* Helper to get how much space the transmit ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the transmit ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t IO::getSpace() const uint16_t IO::getSpace() const
{ {
return m_txBuffer.getSpace(); return m_txBuffer.getSpace();
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="dcd"></param>
void IO::setDecode(bool dcd) void IO::setDecode(bool dcd)
{ {
if (dcd != m_dcd) if (dcd != m_dcd)
@ -193,8 +223,10 @@ void IO::setDecode(bool dcd)
m_dcd = dcd; m_dcd = dcd;
} }
/* Helper to set the modem air interface state. */ /// <summary>
/// Helper to set the modem air interface state.
/// </summary>
/// <param name="modemState"></param>
void IO::setMode(DVM_STATE modemState) void IO::setMode(DVM_STATE modemState)
{ {
DVM_STATE relativeState = modemState; DVM_STATE relativeState = modemState;
@ -203,17 +235,22 @@ void IO::setMode(DVM_STATE modemState)
relativeState = serial.calRelativeState(modemState); relativeState = serial.calRelativeState(modemState);
} }
DEBUG3("IO::setMode() setting modem state", modemState, relativeState); DEBUG3("IO::setMode(): setting modem state", modemState, relativeState);
rf1Conf(relativeState, true); rf1Conf(relativeState, true);
DEBUG4("IO::setMode() setting lights", relativeState == STATE_DMR, relativeState == STATE_P25, relativeState == STATE_NXDN); DEBUG4("IO::setMode(): setting lights", relativeState == STATE_DMR, relativeState == STATE_P25, relativeState == STATE_NXDN);
setDMRInt(relativeState == STATE_DMR); setDMRInt(relativeState == STATE_DMR);
setP25Int(relativeState == STATE_P25); setP25Int(relativeState == STATE_P25);
setNXDNInt(relativeState == STATE_NXDN); setNXDNInt(relativeState == STATE_NXDN);
} }
/* Sets the RF parameters. */ /// <summary>
/// Sets the RF parameters.
/// </summary>
/// <param name="rxFreq"></param>
/// <param name="txFreq"></param>
/// <param name="rfPower"></param>
/// <param name="gainMode"></param>
uint8_t IO::setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode) uint8_t IO::setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode)
{ {
m_rfPower = rfPower >> 2; m_rfPower = rfPower >> 2;
@ -221,21 +258,21 @@ uint8_t IO::setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_G
// check frequency ranges // check frequency ranges
if (!( if (!(
/* 136 - 174 mhz */ /** 136 - 174 mhz */
((rxFreq >= VHF_MIN) && (rxFreq < VHF_MAX)) || ((txFreq >= VHF_MIN) && (txFreq < VHF_MAX)) || ((rxFreq >= VHF_MIN) && (rxFreq < VHF_MAX)) || ((txFreq >= VHF_MIN) && (txFreq < VHF_MAX)) ||
/* 216 - 225 mhz */ /** 216 - 225 mhz */
((rxFreq >= VHF_220_MIN) && (rxFreq < VHF_220_MAX)) || ((txFreq >= VHF_220_MIN) && (txFreq < VHF_220_MAX)) || ((rxFreq >= VHF_220_MIN) && (rxFreq < VHF_220_MAX)) || ((txFreq >= VHF_220_MIN) && (txFreq < VHF_220_MAX)) ||
/* 380 - 431 mhz */ /** 380 - 431 mhz */
((rxFreq >= UHF_380_MIN) && (rxFreq < UHF_380_MAX)) || ((txFreq >= UHF_380_MIN) && (txFreq < UHF_380_MAX)) || ((rxFreq >= UHF_380_MIN) && (rxFreq < UHF_380_MAX)) || ((txFreq >= UHF_380_MIN) && (txFreq < UHF_380_MAX)) ||
/* 431 - 450 mhz */ /** 431 - 450 mhz */
((rxFreq >= UHF_1_MIN) && (rxFreq < UHF_1_MAX)) || ((txFreq >= UHF_1_MIN) && (txFreq < UHF_1_MAX)) || ((rxFreq >= UHF_1_MIN) && (rxFreq < UHF_1_MAX)) || ((txFreq >= UHF_1_MIN) && (txFreq < UHF_1_MAX)) ||
/* 450 - 470 mhz */ /** 450 - 470 mhz */
((rxFreq >= UHF_2_MIN) && (rxFreq < UHF_2_MAX)) || ((txFreq >= UHF_2_MIN) && (txFreq < UHF_2_MAX)) || ((rxFreq >= UHF_2_MIN) && (rxFreq < UHF_2_MAX)) || ((txFreq >= UHF_2_MIN) && (txFreq < UHF_2_MAX)) ||
/* 470 - 520 mhz */ /** 470 - 520 mhz */
((rxFreq >= UHF_T_MIN) && (rxFreq < UHF_T_MAX)) || ((txFreq >= UHF_T_MIN) && (txFreq < UHF_T_MAX)) || ((rxFreq >= UHF_T_MIN) && (rxFreq < UHF_T_MAX)) || ((txFreq >= UHF_T_MIN) && (txFreq < UHF_T_MAX)) ||
/* 842 - 900 mhz */ /** 842 - 900 mhz */
((rxFreq >= UHF_800_MIN) && (rxFreq < UHF_800_MAX)) || ((txFreq >= UHF_800_MIN) && (txFreq < UHF_800_MAX)) || ((rxFreq >= UHF_800_MIN) && (rxFreq < UHF_800_MAX)) || ((txFreq >= UHF_800_MIN) && (txFreq < UHF_800_MAX)) ||
/* 900 - 950 mhz */ /** 900 - 950 mhz */
((rxFreq >= UHF_900_MIN) && (rxFreq < UHF_900_MAX)) || ((txFreq >= UHF_900_MIN) && (txFreq < UHF_900_MAX)) ((rxFreq >= UHF_900_MIN) && (rxFreq < UHF_900_MAX)) || ((txFreq >= UHF_900_MIN) && (txFreq < UHF_900_MAX))
)) ))
return RSN_INVALID_REQUEST; return RSN_INVALID_REQUEST;
@ -268,41 +305,49 @@ uint8_t IO::setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_G
m_rxFrequency = rxFreq; m_rxFrequency = rxFreq;
m_txFrequency = txFreq; m_txFrequency = txFreq;
DEBUG5("IO::setRFParams() setting RF params", m_rxFrequency, m_txFrequency, m_rfPower, m_gainMode); DEBUG5("IO::setRFParams(): setting RF params", m_rxFrequency, m_txFrequency, m_rfPower, m_gainMode);
return RSN_OK; return RSN_OK;
} }
/* Flag indicating the TX ring buffer has overflowed. */ /// <summary>
/// Flag indicating the TX ring buffer has overflowed.
/// </summary>
/// <returns></returns>
bool IO::hasTXOverflow() bool IO::hasTXOverflow()
{ {
return m_txBuffer.hasOverflowed(); return m_txBuffer.hasOverflowed();
} }
/* Flag indicating the RX ring buffer has overflowed. */ /// <summary>
/// Flag indicating the RX ring buffer has overflowed.
/// </summary>
/// <returns></returns>
bool IO::hasRXOverflow() bool IO::hasRXOverflow()
{ {
return m_rxBuffer.hasOverflowed(); return m_rxBuffer.hasOverflowed();
} }
/* */ /// <summary>
///
/// </summary>
void IO::resetWatchdog() void IO::resetWatchdog()
{ {
m_watchdog = 0U; m_watchdog = 0U;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint32_t IO::getWatchdog() uint32_t IO::getWatchdog()
{ {
return m_watchdog; return m_watchdog;
} }
/* */ /// <summary>
///
/// </summary>
void IO::selfTest() void IO::selfTest()
{ {
bool ledValue = false; bool ledValue = false;
@ -396,8 +441,11 @@ void IO::selfTest()
delayUS(125000U); delayUS(125000U);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="int1"></param>
/// <param name="int2"></param>
void IO::getIntCounter(uint16_t& int1, uint16_t& int2) void IO::getIntCounter(uint16_t& int1, uint16_t& int2)
{ {
int1 = m_int1Counter; int1 = m_int1Counter;
@ -411,8 +459,11 @@ void IO::getIntCounter(uint16_t& int1, uint16_t& int2)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/* */ /// <summary>
///
/// </summary>
/// <param name="rxFreq"></param>
/// <param name="txFreq"></param>
void IO::checkBand(uint32_t rxFreq, uint32_t txFreq) void IO::checkBand(uint32_t rxFreq, uint32_t txFreq)
{ {
// check hotspot configuration for single or dual ADF7021 // check hotspot configuration for single or dual ADF7021

375
IO.h

@ -1,25 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2020 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017,2018,2019,2020 Andy Uribe, CA6JAU
* Copyright (C) 2017 Danilo, DB4PLE
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file IO.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file IO.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
* @file IOSTM.cpp * @package DVM / DSP Firmware (Hotspot)
* @ingroup hotspot_fw *
* @file ADF7021.cpp */
* @ingroup hotspot_fw //
*/ // Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2017-2021 Bryan Biedenkapp N2PLL
*
* 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(__IO_H__) #if !defined(__IO_H__)
#define __IO_H__ #define __IO_H__
@ -32,257 +42,128 @@
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#if defined(DUPLEX) #if defined(DUPLEX)
#define CAL_DLY_LOOP 96100U #define CAL_DLY_LOOP 98950U
#else #else
#define CAL_DLY_LOOP 104600U #define CAL_DLY_LOOP 110850U
#endif #endif
/**
* @brief ADF7021 Gain Modes
*/
enum ADF_GAIN_MODE { enum ADF_GAIN_MODE {
ADF_GAIN_AUTO = 0U, //! AGC automatic, default settings // AGC automatic, default settings
ADF_GAIN_AUTO_LIN = 1U, //! AGC automatic with high LNA linearity ADF_GAIN_AUTO = 0U,
ADF_GAIN_LOW = 2U, //! AGC OFF, lowest gain // AGC automatic with high LNA linearity
ADF_GAIN_HIGH = 3U //! AGC OFF, highest gain ADF_GAIN_AUTO_LIN = 1U,
// AGC OFF, lowest gain
ADF_GAIN_LOW = 2U,
// AGC OFF, highest gain
ADF_GAIN_HIGH = 3U
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements the input/output data path with the radio air interface.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements the input/output data path with the radio air interface.
* @ingroup hotspot_fw
*/
class DSP_FW_API IO { class DSP_FW_API IO {
public: public:
/** /// <summary>Initializes a new instance of the IO class.</summary>
* @brief Initializes a new instance of the IO class.
*/
IO(); IO();
/** /// <summary>Starts air interface sampler.</summary>
* @brief Initializes the air interface sampler.
*/
void init();
/**
* @brief Starts air interface sampler.
*/
void start(); void start();
/** /// <summary>Process bits from air interface.</summary>
* @brief Process bits from air interface.
*/
void process(); void process();
/** /// <summary>Write bits to air interface.</summary>
* @brief Write bits to air interface.
* @param data Data to write.
* @param length Length of data buffer.
* @param control
*/
void write(uint8_t* data, uint16_t length, const uint8_t* control = NULL); void write(uint8_t* data, uint16_t length, const uint8_t* control = NULL);
/** /// <summary>Helper to get how much space the transmit ring buffer has for samples.</summary>
* @brief Helper to get how much space the transmit ring buffer has for samples.
* @returns uint16_t Amount of space in the transmit ring buffer for samples.
*/
uint16_t getSpace(void) const; uint16_t getSpace(void) const;
/** /// <summary></summary>
* @brief
* @param dcd
*/
void setDecode(bool dcd); void setDecode(bool dcd);
/** /// <summary>Set modem mode.</summary>
* @brief Set modem mode.
* @param modemState
*/
void setMode(DVM_STATE modemState); void setMode(DVM_STATE modemState);
/** /// <summary>Hardware interrupt handler.</summary>
* @brief Hardware interrupt handler.
*/
void interrupt1(); void interrupt1();
#if defined(DUPLEX) #if defined(DUPLEX)
/** /// <summary>Hardware interrupt handler.</summary>
* @brief Hardware interrupt handler.
*/
void interrupt2(); void interrupt2();
#endif #endif
/** /// <summary>Sets the ADF7021 RF configuration.</summary>
* @brief Sets the ADF7021 RF configuration.
* @param modemState
* @param reset
*/
void rf1Conf(DVM_STATE modemState, bool reset); void rf1Conf(DVM_STATE modemState, bool reset);
#if defined(DUPLEX) #if defined(DUPLEX)
/** /// <summary>Sets the ADF7021 RF configuration.</summary>
* @brief Sets the ADF7021 RF configuration.
* @param modemState
*/
void rf2Conf(DVM_STATE modemState); void rf2Conf(DVM_STATE modemState);
#endif #endif
/** /// <summary></summary>
* @brief Sets the deviation levels.
* @param dmrTXLevel DMR Transmit Level.
* @param p25TXLevel P25 Transmit Level.
* @param nxdnTXLevel NXDN Transmit Level.
*/
void setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel); void setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel);
/** /// <summary>Sets the RF parameters.</summary>
* @brief Sets the RF parameters.
* @param rxFreq Receive Frequency (hz).
* @param txFreq Transmit Frequency (hz).
* @param rfPower RF Power Level.
* @param gainMode Gain Mode.
* @returns uint8_t Reason code.
*/
uint8_t setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode); uint8_t setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode);
/** /// <summary>Sets the RF adjustment parameters.</summary>
* @brief Sets the RF adjustment parameters.
* @param dmrDiscBWAdj DMR Discriminator Bandwidth Adjust.
* @param p25DiscBWAdj P25 Discriminator Bandwidth Adjust.
* @param nxdnDiscBWAdj NXDN Discriminator Bandwidth Adjust.
* @param dmrPostBWAdj DMR Post Bandwidth Adjust.
* @param p25PostBWAdj P25 Post Bandwidth Adjust.
* @param nxdnPostBWAdj NXDN Post Bandwidth Adjust.
*/
void setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWAdj); void setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWAdj);
/** /// <summary>Sets the RF AFC adjustment parameters.</summary>
* @brief Sets the RF AFC adjustment parameters.
* @param afcEnable Flag indicating the Automatic Frequency Correction is enabled.
* @param afcKI
* @param afcKP
* @param afcRange
*/
void setAFCParams(bool afcEnable, uint8_t afcKI, uint8_t afcKP, uint8_t afcRange); void setAFCParams(bool afcEnable, uint8_t afcKI, uint8_t afcKP, uint8_t afcRange);
/** /// <summary>Flag indicating the TX ring buffer has overflowed.</summary>
* @brief Flag indicating the TX ring buffer has overflowed.
* @returns bool Flag indicating the TX ring buffer has overflowed.
*/
bool hasTXOverflow(void); bool hasTXOverflow(void);
/** /// <summary>Flag indicating the RX ring buffer has overflowed.</summary>
* @brief Flag indicating the RX ring buffer has overflowed.
* @returns bool Flag indicating the RX ring buffer has overflowed.
*/
bool hasRXOverflow(void); bool hasRXOverflow(void);
/** /// <summary></summary>
* @brief
*/
void resetWatchdog(void); void resetWatchdog(void);
/** /// <summary></summary>
* @brief
* @returns uint32_t
*/
uint32_t getWatchdog(void); uint32_t getWatchdog(void);
/** /// <summary>Gets the CPU type the firmware is running on.</summary>
* @brief Gets the CPU type the firmware is running on.
* @returns uint8_t
*/
uint8_t getCPU() const; uint8_t getCPU() const;
/** /// <summary>Gets the unique identifier for the air interface.</summary>
* @brief Gets the unique identifier for the air interface.
* @param buffer
*/
void getUDID(uint8_t* buffer); void getUDID(uint8_t* buffer);
/** /// <summary></summary>
* @brief
* @param modemState
*/
void updateCal(DVM_STATE modemState); void updateCal(DVM_STATE modemState);
/** /// <summary></summary>
* @brief
*/
void delayBit(void); void delayBit(void);
/** /// <summary></summary>
* @brief
* @returns uint16_t
*/
uint16_t readRSSI(void); uint16_t readRSSI(void);
/** /// <summary></summary>
* @brief
*/
void selfTest(); void selfTest();
/** /// <summary></summary>
* @brief
*/
void resetMCU();
/**
* @brief
* @param[out] int1
* @param[out] int2
*/
void getIntCounter(uint16_t& int1, uint16_t& int2); void getIntCounter(uint16_t& int1, uint16_t& int2);
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool isDualBand(); bool isDualBand();
#endif #endif
/** /// <summary></summary>
* @brief
* @param on
*/
void SCLK(bool on); void SCLK(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void SDATA(bool on); void SDATA(bool on);
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool SREAD(); bool SREAD();
/** /// <summary></summary>
* @brief
* @param on
*/
void SLE1(bool on); void SLE1(bool on);
#if defined(DUPLEX) #if defined(DUPLEX)
/** /// <summary></summary>
* @brief
* @param on
*/
void SLE2(bool on); void SLE2(bool on);
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool RXD2(); bool RXD2();
#endif #endif
/** /// <summary></summary>
* @brief
* @param on
*/
void CE(bool on); void CE(bool on);
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool RXD1(); bool RXD1();
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool CLK(); bool CLK();
private: private:
@ -304,116 +185,58 @@ private:
uint8_t m_rfPower; uint8_t m_rfPower;
ADF_GAIN_MODE m_gainMode; ADF_GAIN_MODE m_gainMode;
/** /// <summary>Helper to check the frequencies are within band ranges of the ADF7021.</summary>
* @brief Helper to check the frequencies are within band ranges of the ADF7021.
* @param rxFreq Receive Frequency (hz).
* @param txFreq Transmit Frequency (hz).
*/
void checkBand(uint32_t rxFreq, uint32_t txFreq); void checkBand(uint32_t rxFreq, uint32_t txFreq);
#if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/** /// <summary></summary>
* @brief
* @param enable
*/
void setBandVHF(bool enable); void setBandVHF(bool enable);
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool hasSingleADF7021(); bool hasSingleADF7021();
#endif #endif
/** /// <summary></summary>
* @brief
*/
void configureBand(); void configureBand();
/** /// <summary></summary>
* @brief
* @param modemState
*/
void configureTxRx(DVM_STATE modemState); void configureTxRx(DVM_STATE modemState);
/** /// <summary></summary>
* @brief
*/
void setTX(); void setTX();
/** /// <summary></summary>
* @brief
* @param doSle
*/
void setRX(bool doSle = true); void setRX(bool doSle = true);
/** /// <summary></summary>
* @brief
*/
void delayIfCal(); void delayIfCal();
/** /// <summary></summary>
* @brief
*/
void delayReset(); void delayReset();
/** /// <summary></summary>
* @brief
* @param us
*/
void delayUS(uint32_t us); void delayUS(uint32_t us);
// Hardware specific routines // Hardware specific routines
/** /// <summary>Initializes hardware interrupts.</summary>
* @brief Initializes hardware interrupts.
*/
void initInt(); void initInt();
/** /// <summary>Starts hardware interrupts.</summary>
* @brief Starts hardware interrupts.
*/
void startInt(); void startInt();
/** /// <summary></summary>
* @brief
* @param on
*/
void setTXDInt(bool on); void setTXDInt(bool on);
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
/** /// <summary></summary>
* @brief
* @param dir
*/
void setDataDirOut(bool dir); void setDataDirOut(bool dir);
/** /// <summary></summary>
* @brief
* @param on
*/
void setRXDInt(bool on); void setRXDInt(bool on);
#endif #endif
/** /// <summary></summary>
* @brief
* @param on
*/
void setLEDInt(bool on); void setLEDInt(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void setPTTInt(bool on); void setPTTInt(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void setCOSInt(bool on); void setCOSInt(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void setDMRInt(bool on); void setDMRInt(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void setP25Int(bool on); void setP25Int(bool on);
/** /// <summary></summary>
* @brief
* @param on
*/
void setNXDNInt(bool on); void setNXDNInt(bool on);
}; };

@ -1,16 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2016,2017,2018,2019,2020 by Andy Uribe CA6JAU
* * Copyright (C) 2017 by Danilo DB4PLE
* Copyright (C) 2020 Jonathan Naylor, G4KLX * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* Copyright (C) 2016 Jim McLaughlin, KI6ZUM *
* Copyright (C) 2016,2017,2018,2019,2020 Andy Uribe, CA6JAU * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017 Danilo, DB4PLE * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "IO.h" #include "IO.h"
@ -80,7 +100,7 @@
#define PIN_COS_LED GPIO_Pin_13 #define PIN_COS_LED GPIO_Pin_13
#define PORT_COS_LED GPIOB #define PORT_COS_LED GPIOB
#elif defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #elif defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) || defined(LONESTAR_USB)
#define PIN_SCLK GPIO_Pin_5 #define PIN_SCLK GPIO_Pin_5
#define PORT_SCLK GPIOB #define PORT_SCLK GPIOB
@ -142,7 +162,7 @@
#define PIN_DEB GPIO_Pin_9 #define PIN_DEB GPIO_Pin_9
#define PORT_DEB GPIOB #define PORT_DEB GPIOB
#define PIN_NXDN_LED GPIO_Pin_7 #define PIN_NXDN_LED GPIO_Pin_1
#define PORT_NXDN_LED GPIOA #define PORT_NXDN_LED GPIOA
#define PIN_DMR_LED GPIO_Pin_13 #define PIN_DMR_LED GPIO_Pin_13
@ -224,7 +244,7 @@
#define PORT_COS_LED GPIOB #define PORT_COS_LED GPIOB
#else #else
#error "Either PI_HAT_7021_REV_02, ZUMSPOT_ADF7021, MMDVM_HS_HAT_REV12, MMDVM_HS_DUAL_HAT_REV10, NANO_HOTSPOT, NANO_DV_REV11, or SKYBRIDGE_HS need to be defined" #error "Either PI_HAT_7021_REV_02, ZUMSPOT_ADF7021, LONESTAR_USB, MMDVM_HS_HAT_REV12, MMDVM_HS_DUAL_HAT_REV10, NANO_HOTSPOT, NANO_DV_REV11, or SKYBRIDGE_HS need to be defined"
#endif #endif
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -252,7 +272,7 @@ extern "C" {
} }
#endif // BIDIR_DATA_PIN #endif // BIDIR_DATA_PIN
#elif defined(ZUMSPOT_ADF7021) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS) #elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
void EXTI3_IRQHandler(void) { void EXTI3_IRQHandler(void) {
@ -289,8 +309,10 @@ extern "C" {
#endif #endif
} }
/* Function delay_us() from stm32duino project */ /// <summary>
/// Function delay_us() from stm32duino project
/// </summary>
/// <param name="us">Number of microseconds to delay.</param>
static inline void delay_us(uint32_t us) static inline void delay_us(uint32_t us)
{ {
us *= 12; us *= 12;
@ -306,8 +328,9 @@ static inline void delay_us(uint32_t us)
: "r0"); : "r0");
} }
/* */ /// <summary>
///
/// </summary>
static inline void delay_ns() static inline void delay_ns()
{ {
@ -322,115 +345,121 @@ static inline void delay_ns()
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Gets the CPU type the firmware is running on. */ /// <summary>
/// Gets the CPU type the firmware is running on.
/// </summary>
/// <returns></returns>
uint8_t IO::getCPU() const uint8_t IO::getCPU() const
{ {
return CPU_TYPE_STM32; return CPU_TYPE_STM32;
} }
/* Gets the unique identifier for the air interface. */ /// <summary>
/// Gets the unique identifier for the air interface.
/// </summary>
/// <returns></returns>
void IO::getUDID(uint8_t* buffer) void IO::getUDID(uint8_t* buffer)
{ {
::memcpy(buffer, (void*)STM32_UUID, 12U); ::memcpy(buffer, (void*)STM32_UUID, 12U);
} }
/* */ /// <summary>
///
void IO::resetMCU() /// </summary>
{
DEBUG1("reset - bye-bye");
delayUS(250 * 1000);
setLEDInt(false);
setCOSInt(false);
setDMRInt(false);
setP25Int(false);
setNXDNInt(false);
delayUS(250 * 1000);
NVIC_SystemReset();
}
/* */
void IO::delayBit() void IO::delayBit()
{ {
delay_ns(); delay_ns();
} }
#if defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::isDualBand() bool IO::isDualBand()
{ {
return GPIO_ReadInputDataBit(PORT_DL_DPX, PIN_DL_DPX) == Bit_SET; return GPIO_ReadInputDataBit(PORT_DL_DPX, PIN_DL_DPX) == Bit_SET;
} }
#endif #endif
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SCLK(bool on) void IO::SCLK(bool on)
{ {
GPIO_WriteBit(PORT_SCLK, PIN_SCLK, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_SCLK, PIN_SCLK, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SDATA(bool on) void IO::SDATA(bool on)
{ {
GPIO_WriteBit(PORT_SDATA, PIN_SDATA, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_SDATA, PIN_SDATA, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::SREAD() bool IO::SREAD()
{ {
return GPIO_ReadInputDataBit(PORT_SREAD, PIN_SREAD) == Bit_SET; return GPIO_ReadInputDataBit(PORT_SREAD, PIN_SREAD) == Bit_SET;
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SLE1(bool on) void IO::SLE1(bool on)
{ {
GPIO_WriteBit(PORT_SLE, PIN_SLE, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_SLE, PIN_SLE, on ? Bit_SET : Bit_RESET);
} }
#if defined(DUPLEX) #if defined(DUPLEX)
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::SLE2(bool on) void IO::SLE2(bool on)
{ {
GPIO_WriteBit(PORT_SLE2, PIN_SLE2, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_SLE2, PIN_SLE2, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::RXD2() bool IO::RXD2()
{ {
return GPIO_ReadInputDataBit(PORT_RXD2, PIN_RXD2) == Bit_SET; return GPIO_ReadInputDataBit(PORT_RXD2, PIN_RXD2) == Bit_SET;
} }
#endif #endif
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::CE(bool on) void IO::CE(bool on)
{ {
GPIO_WriteBit(PORT_CE, PIN_CE, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_CE, PIN_CE, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::RXD1() bool IO::RXD1()
{ {
return GPIO_ReadInputDataBit(PORT_RXD, PIN_RXD) == Bit_SET; return GPIO_ReadInputDataBit(PORT_RXD, PIN_RXD) == Bit_SET;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::CLK() bool IO::CLK()
{ {
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
@ -444,45 +473,54 @@ bool IO::CLK()
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
#if defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
/* */ /// <summary>
///
/// </summary>
/// <param name="enable"></param>
void IO::setBandVHF(bool enable) void IO::setBandVHF(bool enable)
{ {
GPIO_WriteBit(PORT_SET_BAND, PIN_SET_BAND, enable ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_SET_BAND, PIN_SET_BAND, enable ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
bool IO::hasSingleADF7021() bool IO::hasSingleADF7021()
{ {
return GPIO_ReadInputDataBit(PORT_SGL_DBL, PIN_SGL_DBL) == Bit_SET; return GPIO_ReadInputDataBit(PORT_SGL_DBL, PIN_SGL_DBL) == Bit_SET;
} }
#endif #endif
/* */ /// <summary>
///
/// </summary>
void IO::delayIfCal() void IO::delayIfCal()
{ {
delayUS(10000); delayUS(10000);
} }
/* */ /// <summary>
///
/// </summary>
void IO::delayReset() void IO::delayReset()
{ {
delayUS(300); delayUS(300);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="us"></param>
void IO::delayUS(uint32_t us) void IO::delayUS(uint32_t us)
{ {
::delay_us(us); ::delay_us(us);
} }
/* Initializes hardware interrupts. */ /// <summary>
/// Initializes hardware interrupts.
/// </summary>
void IO::initInt() void IO::initInt()
{ {
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitTypeDef GPIO_InitStruct;
@ -497,11 +535,11 @@ void IO::initInt()
#if defined(PI_HAT_7021_REV_02) #if defined(PI_HAT_7021_REV_02)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
#elif defined(ZUMSPOT_ADF7021) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS) #elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
#endif #endif
#if defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
// Pin defines if the board has a single ADF7021 or double // Pin defines if the board has a single ADF7021 or double
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SGL_DBL; GPIO_InitStruct.GPIO_Pin = PIN_SGL_DBL;
@ -524,12 +562,21 @@ void IO::initInt()
#endif #endif
volatile unsigned int delay; // Pin PA11,PA12 = LOW, USB Reset
for (delay = 0; delay < 512; delay++); GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_WriteBit(GPIOA, GPIO_Pin_11, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_12, Bit_RESET);
#if defined(LONG_USB_RESET)
// 10 ms delay
delayUS(10000U);
#else
volatile unsigned int delay;
for (delay = 0; delay < 512; delay++);
#endif
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
@ -664,7 +711,7 @@ void IO::initInt()
EXTI_InitStructure.EXTI_Line = EXTI_Line14; EXTI_InitStructure.EXTI_Line = EXTI_Line14;
#endif #endif
#elif defined(ZUMSPOT_ADF7021) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS) #elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
// Connect EXTI3 Line // Connect EXTI3 Line
@ -682,7 +729,7 @@ void IO::initInt()
// Connect EXTI5 Line // Connect EXTI5 Line
GPIO_EXTILineConfig(PORT_TXD2_INT, PIN_TXD2_INT); GPIO_EXTILineConfig(PORT_TXD2_INT, PIN_TXD2_INT);
// Configure EXT5 line // Configure EXT5 line
#if defined(ZUMSPOT_ADF7021) || defined(SKYBRIDGE_HS) #if defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)
EXTI_InitStructure2.EXTI_Line = EXTI_Line8; EXTI_InitStructure2.EXTI_Line = EXTI_Line8;
#else #else
EXTI_InitStructure2.EXTI_Line = EXTI_Line5; EXTI_InitStructure2.EXTI_Line = EXTI_Line5;
@ -704,8 +751,9 @@ void IO::initInt()
#endif #endif
} }
/* Starts hardware interrupts. */ /// <summary>
/// Starts hardware interrupts.
/// </summary>
void IO::startInt() void IO::startInt()
{ {
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
@ -718,7 +766,7 @@ void IO::startInt()
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
#elif defined(ZUMSPOT_ADF7021) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS) #elif defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(LIBRE_KIT_ADF7021) || defined(MMDVM_HS_HAT_REV12) || defined(MMDVM_HS_DUAL_HAT_REV10) || defined(NANO_HOTSPOT) || defined(NANO_DV_REV11) || defined(D2RG_MMDVM_HS) || defined(SKYBRIDGE_HS)
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
// Enable and set EXTI3 Interrupt // Enable and set EXTI3 Interrupt
@ -749,8 +797,11 @@ void IO::startInt()
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
/* */ /// <summary>
///
/// </summary>
/// <remarks>RXD pin is bidirectional in standard interfaces</remarks>
/// <param name="dir"></param>
void IO::setDataDirOut(bool dir) void IO::setDataDirOut(bool dir)
{ {
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitTypeDef GPIO_InitStruct;
@ -768,58 +819,74 @@ void IO::setDataDirOut(bool dir)
#endif #endif
#if defined(BIDIR_DATA_PIN) #if defined(BIDIR_DATA_PIN)
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setRXDInt(bool on) void IO::setRXDInt(bool on)
{ {
GPIO_WriteBit(PORT_RXD, PIN_RXD, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_RXD, PIN_RXD, on ? Bit_SET : Bit_RESET);
} }
#endif #endif
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setTXDInt(bool on) void IO::setTXDInt(bool on)
{ {
GPIO_WriteBit(PORT_TXD, PIN_TXD, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_TXD, PIN_TXD, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setLEDInt(bool on) void IO::setLEDInt(bool on)
{ {
GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_LED, PIN_LED, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setPTTInt(bool on) void IO::setPTTInt(bool on)
{ {
GPIO_WriteBit(PORT_PTT_LED, PIN_PTT_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_PTT_LED, PIN_PTT_LED, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setCOSInt(bool on) void IO::setCOSInt(bool on)
{ {
GPIO_WriteBit(PORT_COS_LED, PIN_COS_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_COS_LED, PIN_COS_LED, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setDMRInt(bool on) void IO::setDMRInt(bool on)
{ {
GPIO_WriteBit(PORT_DMR_LED, PIN_DMR_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_DMR_LED, PIN_DMR_LED, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setP25Int(bool on) void IO::setP25Int(bool on)
{ {
GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET);
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setNXDNInt(bool on) void IO::setNXDNInt(bool on)
{ {
GPIO_WriteBit(PORT_NXDN_LED, PIN_NXDN_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_NXDN_LED, PIN_NXDN_LED, on ? Bit_SET : Bit_RESET);

@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

@ -15,7 +15,10 @@ OBJDIR_F4=obj_f4
BINELF_F1=dvm-firmware-hs_f1.elf BINELF_F1=dvm-firmware-hs_f1.elf
BINHEX_F1=dvm-firmware-hs_f1.hex BINHEX_F1=dvm-firmware-hs_f1.hex
BINBIN_F1=dvm-firmware-hs_f1.bin BINBIN_F1=dvm-firmware-hs_f1.bin
BINELF_F4=dvm-firmware-hs_f4.elf BINELF_F1BL=dvm-firmware-hs_f1bl.elf
BINHEX_F1BL=dvm-firmware-hs_f1bl.hex
BINBIN_F1BL=dvm-firmware-hs_f1bl.bin
BINELF_F4=dvm-firwmare-hs_f4.elf
BINHEX_F4=dvm-firmware-hs_f4.hex BINHEX_F4=dvm-firmware-hs_f4.hex
BINBIN_F4=dvm-firmware-hs_f4.bin BINBIN_F4=dvm-firmware-hs_f4.bin
@ -78,6 +81,7 @@ CSRC_STD_F4=$(wildcard $(STD_LIB_F4)/*.c)
SYS_F4=$(wildcard $(SYS_DIR_F4)/*.c) SYS_F4=$(wildcard $(SYS_DIR_F4)/*.c)
STARTUP_F4=$(wildcard $(STARTUP_DIR_F4)/*.c) STARTUP_F4=$(wildcard $(STARTUP_DIR_F4)/*.c)
OBJ_F1=$(CXXSRC:./%.cpp=$(OBJDIR_F1)/%.o) $(CSRC_STD_F1:$(STD_LIB_F1)/%.c=$(OBJDIR_F1)/%.o) $(SYS_F1:$(SYS_DIR_F1)/%.c=$(OBJDIR_F1)/%.o) $(STARTUP_F1:$(STARTUP_DIR_F1)/%.c=$(OBJDIR_F1)/%.o) OBJ_F1=$(CXXSRC:./%.cpp=$(OBJDIR_F1)/%.o) $(CSRC_STD_F1:$(STD_LIB_F1)/%.c=$(OBJDIR_F1)/%.o) $(SYS_F1:$(SYS_DIR_F1)/%.c=$(OBJDIR_F1)/%.o) $(STARTUP_F1:$(STARTUP_DIR_F1)/%.c=$(OBJDIR_F1)/%.o)
OBJ_F1BL=$(CXXSRC:./%.cpp=$(OBJDIR_F1)/%.o) $(CSRC_STD_F1:$(STD_LIB_F1)/%.c=$(OBJDIR_F1)/%.o) $(SYS_F1:$(SYS_DIR_F1)/%.c=$(OBJDIR_F1)/%.o) $(STARTUP_F1:$(STARTUP_DIR_F1)/%.c=$(OBJDIR_F1)/%.o) $(CXX_USB_F1:$(USB_F1)/%.cpp=$(OBJDIR_F1)/%.o) $(C_USB_F1:$(USB_F1)/%.c=$(OBJDIR_F1)/%.o)
OBJ_F4=$(CXXSRC:./%.cpp=$(OBJDIR_F4)/%.o) $(CSRC_STD_F4:$(STD_LIB_F4)/%.c=$(OBJDIR_F4)/%.o) $(SYS_F4:$(SYS_DIR_F4)/%.c=$(OBJDIR_F4)/%.o) $(STARTUP_F4:$(STARTUP_DIR_F4)/%.c=$(OBJDIR_F4)/%.o) OBJ_F4=$(CXXSRC:./%.cpp=$(OBJDIR_F4)/%.o) $(CSRC_STD_F4:$(STD_LIB_F4)/%.c=$(OBJDIR_F4)/%.o) $(SYS_F4:$(SYS_DIR_F4)/%.c=$(OBJDIR_F4)/%.o) $(STARTUP_F4:$(STARTUP_DIR_F4)/%.c=$(OBJDIR_F4)/%.o)
# MCU flags # MCU flags
@ -86,6 +90,7 @@ MCFLAGS_F4=-mcpu=cortex-m4 -mthumb -mlittle-endian -mfpu=fpv4-sp-d16 -mfloat-abi
# Compile flags # Compile flags
DEFS_F1_HS=-DUSE_STDPERIPH_DRIVER -DSTM32F10X_MD -DHSE_VALUE=$(OSC) -DVECT_TAB_OFFSET=0x0 -DMADEBYMAKEFILE DEFS_F1_HS=-DUSE_STDPERIPH_DRIVER -DSTM32F10X_MD -DHSE_VALUE=$(OSC) -DVECT_TAB_OFFSET=0x0 -DMADEBYMAKEFILE
DEFS_F1_HS_BL=-DUSE_STDPERIPH_DRIVER -DSTM32F10X_MD -DHSE_VALUE=$(OSC) -DVECT_TAB_OFFSET=0x2000 -DMADEBYMAKEFILE
# STM32F446 Pi-Hat board: # STM32F446 Pi-Hat board:
DEFS_PI_F4=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE DEFS_PI_F4=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DSTM32F446xx -DSTM32F4_PI -DHSE_VALUE=$(OSC) -DMADEBYMAKEFILE
# STM32F4 Nucleo-64 F446RE board: # STM32F4 Nucleo-64 F446RE board:
@ -99,6 +104,7 @@ CXXFLAGS_F4=-c $(MCFLAGS_F4) $(INCLUDES_F4)
# Linker flags # Linker flags
LDFLAGS_F1_N =-T stm32f10x_normal.ld $(MCFLAGS_F1) $(INCLUDES_LIBS_F1) LDFLAGS_F1_N =-T stm32f10x_normal.ld $(MCFLAGS_F1) $(INCLUDES_LIBS_F1)
LDFLAGS_F1_D =-T stm32f10x_debug.ld $(MCFLAGS_F1) $(INCLUDES_LIBS_F1) LDFLAGS_F1_D =-T stm32f10x_debug.ld $(MCFLAGS_F1) $(INCLUDES_LIBS_F1)
LDFLAGS_F1_BL =-T stm32f10x_bootloader.ld $(MCFLAGS_F1) $(INCLUDES_LIBS_F1)
LDFLAGS_F4 =-T stm32f4xx_link.ld $(MCFLAGS_F4) $(INCLUDES_LIBS_F4) LDFLAGS_F4 =-T stm32f4xx_link.ld $(MCFLAGS_F4) $(INCLUDES_LIBS_F4)
LDFLAGS_F4_D =-T stm32f4xx_link_debug.ld $(MCFLAGS_F4) $(INCLUDES_LIBS_F4) LDFLAGS_F4_D =-T stm32f4xx_link_debug.ld $(MCFLAGS_F4) $(INCLUDES_LIBS_F4)
@ -108,52 +114,38 @@ CXXFLAGS=-Os -g -fno-exceptions -ffunction-sections -fdata-sections -fno-builtin
LDFLAGS=-Os -g --specs=nano.specs --specs=nosys.specs LDFLAGS=-Os -g --specs=nano.specs --specs=nosys.specs
# Build Rules # Build Rules
.PHONY: all release_f1 release_f4 hs pi-f4 f446 clean .PHONY: all release_f1bl bl f446 clean
all: hs all: hs
#error "Either PI_HAT_7021_REV_02, ZUMSPOT_ADF7021, LONESTAR_USB, MMDVM_HS_HAT_REV12, MMDVM_HS_DUAL_HAT_REV10, NANO_HOTSPOT, NANO_DV_REV11, or SKYBRIDGE_HS #error "Either PI_HAT_7021_REV_02, ZUMSPOT_ADF7021, LONESTAR_USB, MMDVM_HS_HAT_REV12, MMDVM_HS_DUAL_HAT_REV10, NANO_HOTSPOT, NANO_DV_REV11, or SKYBRIDGE_HS
zumspot-adf7021: CFLAGS+=-DZUMSPOT_ADF7021 -DSTM32_USART1_HOST zumspot-adf7021: CFLAGS+=-DZUMSPOT_ADF7021 -DSTM32_USB_HOST
zumspot-adf7021: CXXFLAGS+=-DZUMSPOT_ADF7021 -DSTM32_USART1_HOST zumspot-adf7021: CXXFLAGS+=-DZUMSPOT_ADF7021 -DSTM32_USB_HOST
zumspot-adf7021: hs zumspot-adf7021: bl
zumspot-adf7021-duplex: CFLAGS+=-DDUPLEX zumspot-adf7021-duplex: CFLAGS+=-DDUPLEX
zumspot-adf7021-duplex: CXXFLAGS+=-DDUPLEX zumspot-adf7021-duplex: CXXFLAGS+=-DDUPLEX
zumspot-adf7021-duplex: zumspot-adf7021 zumspot-adf7021-duplex: zumspot-adf7021
pihat-7021-r2: CFLAGS+=-DPI_HAT_7021_REV_02 -DSTM32_USART1_HOST lonestar-usb: CFLAGS+=-DLONESTAR_USB -DSTM32_USB_HOST
pihat-7021-r2: CXXFLAGS+=-DZUMSPOT_ADF7021 -DSTM32_USART1_HOST lonestar-usb: CXXFLAGS+=-DLONESTAR_USB -DSTM32_USB_HOST
pihat-7021-r2: hs lonestar-usb: bl
pihat-7021-r2-duplex: CFLAGS+=-DDUPLEX
pihat-7021-r2-duplex: CXXFLAGS+=-DDUPLEX mmdvm-hs-hat: CFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USB_HOST
pihat-7021-r2-duplex: pihat-7021-r2 mmdvm-hs-hat: CXXFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USB_HOST
mmdvm-hs-hat: bl
mmdvm-hs-hat: CFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USART1_HOST
mmdvm-hs-hat: CXXFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USART1_HOST
mmdvm-hs-hat: hs
mmdvm-hs-hat-debug: CFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USART1_HOST
mmdvm-hs-hat-debug: CXXFLAGS+=-DMMDVM_HS_HAT_REV12 -DSTM32_USART1_HOST
mmdvm-hs-hat-debug: hs-debug
mmdvm-hs-hat-dual: CFLAGS+=-DDUPLEX mmdvm-hs-hat-dual: CFLAGS+=-DDUPLEX
mmdvm-hs-hat-dual: CXXFLAGS+=-DDUPLEX mmdvm-hs-hat-dual: CXXFLAGS+=-DDUPLEX
mmdvm-hs-hat-dual: mmdvm-hs-hat mmdvm-hs-hat-dual: mmdvm-hs-hat
mmdvm-hs-hat-dual-debug: CFLAGS+=-DDUPLEX
mmdvm-hs-hat-dual-debug: CXXFLAGS+=-DDUPLEX
mmdvm-hs-hat-dual-debug: mmdvm-hs-hat-debug
hs: CFLAGS+=$(CFLAGS_F1) $(DEFS_F1_HS)
hs: CXXFLAGS+=$(CXXFLAGS_F1) $(DEFS_F1_HS)
hs: LDFLAGS+=$(LDFLAGS_F1_N)
hs: release_f1
hs-debug: CFLAGS+=$(CFLAGS_F1) $(DEFS_F1_HS) bl: CFLAGS+=$(CFLAGS_F1) $(DEFS_F1_HS_BL)
hs-debug: CXXFLAGS+=$(CXXFLAGS_F1) $(DEFS_F1_HS) bl: CXXFLAGS+=$(CXXFLAGS_F1) $(DEFS_F1_HS_BL)
hs-debug: LDFLAGS+=$(LDFLAGS_F1_D) bl: LDFLAGS+=$(LDFLAGS_F1_BL)
hs-debug: release_f1 bl: release_f1bl
release_f1: $(BINDIR) release_f1bl: $(BINDIR)
release_f1: $(OBJDIR_F1) release_f1bl: $(OBJDIR_F1)
release_f1: $(BINDIR)/$(BINBIN_F1) release_f1bl: $(BINDIR)/$(BINBIN_F1BL)
release_f4: $(BINDIR) release_f4: $(BINDIR)
release_f4: $(OBJDIR_F4) release_f4: $(OBJDIR_F4)
@ -172,11 +164,11 @@ $(OBJDIR_F4):
mkdir $@/p25 mkdir $@/p25
mkdir $@/nxdn mkdir $@/nxdn
$(BINDIR)/$(BINBIN_F1): $(BINDIR)/$(BINELF_F1) $(BINDIR)/$(BINBIN_F1BL): $(BINDIR)/$(BINELF_F1BL)
$(CP) -O binary $< $@ $(CP) -O binary $< $@
$(BINDIR)/$(BINELF_F1): $(OBJ_F1) $(BINDIR)/$(BINELF_F1BL): $(OBJ_F1BL)
$(CXX) $(OBJ_F1) $(LDFLAGS) -o $@ $(CXX) $(OBJ_F1BL) $(LDFLAGS) -o $@
$(SIZE) $(BINDIR)/$(BINELF_F1) $(SIZE) $(BINDIR)/$(BINELF_F1BL)
$(BINDIR)/$(BINBIN_F4): $(BINDIR)/$(BINELF_F4) $(BINDIR)/$(BINBIN_F4): $(BINDIR)/$(BINELF_F4)
$(CP) -O binary $< $@ $(CP) -O binary $< $@
@ -212,3 +204,4 @@ clean:
test ! -d $(OBJDIR_F1) || rm -rf $(OBJDIR_F1) test ! -d $(OBJDIR_F1) || rm -rf $(OBJDIR_F1)
test ! -d $(OBJDIR_F4) || rm -rf $(OBJDIR_F4) test ! -d $(OBJDIR_F4) || rm -rf $(OBJDIR_F4)
rm -f $(BINDIR)/*.bin $(BINDIR)/*.elf rm -f $(BINDIR)/*.bin $(BINDIR)/*.elf

@ -21,36 +21,51 @@ An example of this would be ```make -f Makefile.STM32FX mmdvm-hs-hat-dual``` for
## Firmware installation ## Firmware installation
The device can be used on top on a RPi attached via the GPIO port or standalone and connected via USB (see usb-support branch). Both variants require different handling of compiling and uploading the firmware, examples on flashing devices are mostly not included here because the methods to flash vary from device to device. The device can be used connected via USB.
### Install the firmware via GPIO on Raspberry Pi The USB connection requires firmware with bootloader. For USB connection a bootloader has to be installed initally. This requires STlink connection. After that is done the firmware upgrade can be done via the USB connection. The STlink connection can be used as fallback if wrongly configured firmware was installed for example.
> **_NOTE:_** Your mileage may vary with these instructions, the hotspot boards are loosely designed around a common factor but not all are created equally. ### Install the firmware with bootloader support for USB connection
First you will need to disable the serial console and disable bluetooth. Edit ```/boot/cmdline.txt``` and remove the line ```console=serial0, 115200```. If you want to use the device via USB port you have to install a bootloader and build the firmware with bootloader support. As the raw device cannot be used with USB you have to use a USB-serial adapter or STlink device.
Next, you will need to disable bluetooth on the board. Edit ```/boot/config.txt``` and add a line containing ```dtoverlay=disable-bt```. Reboot.
> Most sets of instructions reccomend to download stm32flash from online, however we have found the prepackaged version to work fine. * The bootloader (https://github.com/DVMProject/STM32F10X_Platform/blob/527fee72ae2291486304380cb812c48f36122c32/utils/bootloader/generic_boot20_pc13.bin) should be installed starting at offset 0x8000000.
* The firmware should be installed starting at offset 0x8002000.
Once the hotspot is back on, navigate to the build folder where you compiled the firmware. Put a jumper across the J1 points on the board, and the RED heartbeat LED should stop flashing. Run the below command to flash. Sudo is required on most systems to access GPIO pins. An example Using STlink this can be done as follows:
```sudo stm32flash -v -w dvm-firmware-hs_f1.bin -i 20,-21,21,-20 -R /dev/ttyAMA0```
Note that on newer raspbian versions, the way GPIO chips are numbered has changed. If you're using raspbian bookworm (debian 12) or greater, use this command instead:
```sudo stm32flash -v -w dvm-firmware-hs_f1.bin -i 532,-533,533,-520 -R /dev/ttyAMA0```
You should see the below output if the board flashed successfully.
``` ```
Wrote and verified address 0x0800be40 (100.00%) Done. user@host:~/dvmfirmware-hs$ -f Makefile.STM32FX mmdvm-hs-hat-usb-dual
...
Resetting device... user@host:~/dvmfirmware-hs$ ./STM32F10X_Platform/utils/linux64/st-flash write ./STM32F10X_Platform/utils/bootloader/generic_boot20_pc13.bin 0x8000000
Reset done. 2018-03-02T10:01:04 INFO src/usb.c: -- exit_dfu_mode
2018-03-02T10:01:04 INFO src/common.c: Loading device parameters....
2018-03-02T10:01:04 INFO src/common.c: Device connected is: F1 Medium-density device, id 0x20036410
2018-03-02T10:01:04 INFO src/common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x10000 bytes (64 KiB) in pages of 1024 bytes
2018-03-02T10:01:04 INFO src/common.c: Attempting to write 7160 (0x1bf8) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x08001800 erased
2018-03-02T10:01:04 INFO src/common.c: Finished erasing 7 pages of 1024 (0x400) bytes
2018-03-02T10:01:04 INFO src/common.c: Starting Flash write for VL/F0/F3 core id
2018-03-02T10:01:04 INFO src/common.c: Successfully loaded flash loader in sram
6/6 pages written
2018-03-02T10:01:05 INFO src/common.c: Starting verification of write complete
2018-03-02T10:01:05 INFO src/common.c: Flash written and verified! jolly good!
user@host:~/dvmfirmware-hs$ ./STM32F10X_Platform/utils/linux64/st-flash write dvm-firmware-hs_f1bl.bin 0x8002000
2018-03-02T10:01:05 INFO src/common.c: Loading device parameters....
2018-03-02T10:01:05 INFO src/common.c: Device connected is: F1 Medium-density device, id 0x20036410
2018-03-02T10:01:05 INFO src/common.c: SRAM size: 0x5000 bytes (20 KiB), Flash: 0x10000 bytes (64 KiB) in pages of 1024 bytes
2018-03-02T10:01:05 INFO src/common.c: Attempting to write 55016 (0xd6e8) bytes to stm32 address: 134225920 (0x8002000)
Flash page at addr: 0x0800f400 erased
2018-03-02T10:01:07 INFO src/common.c: Finished erasing 54 pages of 1024 (0x400) bytes
2018-03-02T10:01:07 INFO src/common.c: Starting Flash write for VL/F0/F3 core id
2018-03-02T10:01:07 INFO src/common.c: Successfully loaded flash loader in sram
53/53 pages written
2018-03-02T10:01:12 INFO src/common.c: Starting verification of write complete
2018-03-02T10:01:13 INFO src/common.c: Flash written and verified! jolly good!
``` ```
The device should now be usable as /dev/ttyACMx.
## Notes ## Notes
**USB Support Note**: See the usb-support branch for the version of this firmware that supports USB.
**NXDN Support Note**: NXDN support is currently experimental. **NXDN Support Note**: NXDN support is currently experimental.
## License ## License

@ -1,13 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (c) 2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (c) 2020 by Geoffrey Merck F4FXL - KC3FRA
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* * This program is free software; you can redistribute it and/or modify
* Copyright (c) 2020 Jonathan Naylor, G4KLX * it under the terms of the GNU General Public License as published by
* Copyright (c) 2020 Geoffrey Merck, F4FXL - KC3FRA * 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(STM32F10X_MD) || defined(STM32F4XX) #if defined(STM32F10X_MD) || defined(STM32F4XX)
#include "STM_UART.h" #include "STM_UART.h"
@ -16,23 +37,25 @@
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the STM_UART class. */ /// <summary>
/// Initializes a new instance of the STM_UART class.
/// </summary>
STM_UART::STM_UART() : STM_UART::STM_UART() :
m_usart(NULL) m_usart(NULL)
{ {
/* stub */ /* stub */
} }
/* */ /// <summary></summary>
/// <param name="usart"></param>
void STM_UART::init(USART_TypeDef* usart) void STM_UART::init(USART_TypeDef* usart)
{ {
m_usart = usart; m_usart = usart;
} }
/* */ /// <summary></summary>
/// <param name="data"></param>
/// <param name="length"></param>
void STM_UART::write(const uint8_t* data, uint16_t length) void STM_UART::write(const uint8_t* data, uint16_t length)
{ {
if (length == 0U || m_usart == NULL) if (length == 0U || m_usart == NULL)
@ -49,15 +72,14 @@ void STM_UART::write(const uint8_t* data, uint16_t length)
USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//make sure TX IRQ is on USART_ITConfig(m_usart, USART_IT_TXE, ENABLE);//make sure TX IRQ is on
} }
/* */ /// <summary></summary>
/// <returns></returns>
uint8_t STM_UART::read() uint8_t STM_UART::read()
{ {
return m_rxFifo.get(); return m_rxFifo.get();
} }
/* */ /// <summary></summary>
void STM_UART::handleIRQ() void STM_UART::handleIRQ()
{ {
if (m_usart == NULL) if (m_usart == NULL)
@ -80,8 +102,12 @@ void STM_UART::handleIRQ()
} }
} }
/* Flushes the transmit shift register. */ /// <summary>
/// Flushes the transmit shift register.
/// </summary>
/// <remarks>
/// This call is blocking!
/// </remarks>
void STM_UART::flush() void STM_UART::flush()
{ {
if (m_usart == NULL) if (m_usart == NULL)
@ -92,15 +118,15 @@ void STM_UART::flush()
; ;
} }
/* */ /// <summary></summary>
/// <returns></returns>
uint16_t STM_UART::available() uint16_t STM_UART::available()
{ {
return m_rxFifo.isEmpty() ? 0U : 1U; return m_rxFifo.isEmpty() ? 0U : 1U;
} }
/* */ /// <summary></summary>
/// <returns></returns>
uint16_t STM_UART::availableForWrite() uint16_t STM_UART::availableForWrite()
{ {
return m_txFifo.isFull() ? 0U : 1U; return m_txFifo.isFull() ? 0U : 1U;

@ -1,19 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (c) 2020 Jonathan Naylor, G4KLX
* Copyright (c) 2020 Geoffrey Merck, F4FXL - KC3FRA
*
*/
/** /**
* @file STM_UART.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file STM_UART.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* 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(STM32F10X_MD) || defined(STM32F4XX) #if defined(STM32F10X_MD) || defined(STM32F4XX)
#if !defined(__STM_UART_H__) #if !defined(__STM_UART_H__)
#define __STM_UART_H__ #define __STM_UART_H__
@ -31,17 +46,12 @@ const uint16_t BUFFER_MASK = BUFFER_SIZE - 1;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// This class represents a FIFO buffer on a STM32 UART.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief This class represents a FIFO buffer on a STM32 UART.
* @ingroup hotspot_fw
*/
class DSP_FW_API STM_UARTFIFO { class DSP_FW_API STM_UARTFIFO {
public: public:
/** /// <summary>Initializes a new instance of the STM_UARTFIFO class.</summary>
* @brief Initializes a new instance of the STM_UARTFIFO class.
*/
STM_UARTFIFO() : STM_UARTFIFO() :
m_head(0U), m_head(0U),
m_tail(0U) m_tail(0U)
@ -49,46 +59,32 @@ public:
/* stub */ /* stub */
} }
/** /// <summary></summary>
* @brief
* @returns uint8_t
*/
uint8_t get() uint8_t get()
{ {
return m_buffer[BUFFER_MASK & (m_tail++)]; return m_buffer[BUFFER_MASK & (m_tail++)];
} }
/** /// <summary></summary>
* @brief
* @param data
*/
void put(uint8_t data) void put(uint8_t data)
{ {
m_buffer[BUFFER_MASK & (m_head++)] = data; m_buffer[BUFFER_MASK & (m_head++)] = data;
} }
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset() void reset()
{ {
m_tail = 0U; m_tail = 0U;
m_head = 0U; m_head = 0U;
} }
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool isEmpty() bool isEmpty()
{ {
return m_tail == m_head; return m_tail == m_head;
} }
/** /// <summary></summary>
* @brief
* @returns bool
*/
bool isFull() bool isFull()
{ {
return ((m_head + 1U) & BUFFER_MASK) == (m_tail & BUFFER_MASK); return ((m_head + 1U) & BUFFER_MASK) == (m_tail & BUFFER_MASK);
@ -102,59 +98,32 @@ private:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// This class represents an STM32 UART.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief This class represents an STM32 UART.
* @ingroup hotspot_fw
*/
class STM_UART { class STM_UART {
public: public:
/** /// <summary>Initializes a new instance of the STM_UART class.</summary>
* @brief Initializes a new instance of the STM_UART class.
*/
STM_UART(); STM_UART();
/** /// <summary></summary>
* @brief Initializes the UART.
* @param usart
*/
void init(USART_TypeDef* usart); void init(USART_TypeDef* usart);
/** /// <summary></summary>
* @brief
* @returns uint8_t
*/
uint8_t read(); uint8_t read();
/** /// <summary></summary>
* @brief
* @param[in] data
* @param length
*/
void write(const uint8_t* data, uint16_t length); void write(const uint8_t* data, uint16_t length);
/** /// <summary></summary>
* @brief
*/
void handleIRQ(); void handleIRQ();
/** /// <summary>Flushes the transmit shift register.</summary>
* @brief Flushes the transmit shift register.
*
* This call is blocking!
*/
void flush(); void flush();
/** /// <summary></summary>
* @brief
* @returns uint16_t
*/
uint16_t available(); uint16_t available();
/** /// <summary></summary>
* @brief
* @returns uint16_t
*/
uint16_t availableForWrite(); uint16_t availableForWrite();
private: private:

@ -1,22 +1,45 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2022 by Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * This library is free software; you can redistribute it and/or
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin, KI6ZUM * modify it under the terms of the GNU Library General Public
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * 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 "SerialBuffer.h" #include "SerialBuffer.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the SerialBuffer class. */ /// <summary>
/// Initializes a new instance of the SerialBuffer class.
/// </summary>
SerialBuffer::SerialBuffer(uint16_t length) : SerialBuffer::SerialBuffer(uint16_t length) :
m_length(length), m_length(length),
m_buffer(NULL), m_buffer(NULL),
@ -27,15 +50,18 @@ SerialBuffer::SerialBuffer(uint16_t length) :
m_buffer = new uint8_t[length]; m_buffer = new uint8_t[length];
} }
/* Finalizes a instance of the SerialBuffer class. */ /// <summary>
/// Finalizes a instance of the SerialBuffer class.
/// </summary>
SerialBuffer::~SerialBuffer() SerialBuffer::~SerialBuffer()
{ {
delete[] m_buffer; delete[] m_buffer;
} }
/* Helper to get how much space the ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t SerialBuffer::getSpace() const uint16_t SerialBuffer::getSpace() const
{ {
uint16_t n = 0U; uint16_t n = 0U;
@ -53,8 +79,10 @@ uint16_t SerialBuffer::getSpace() const
return n; return n;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint16_t SerialBuffer::getData() const uint16_t SerialBuffer::getData() const
{ {
if (m_tail == m_head) if (m_tail == m_head)
@ -65,8 +93,9 @@ uint16_t SerialBuffer::getData() const
return m_length - m_tail + m_head; return m_length - m_tail + m_head;
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void SerialBuffer::reset() void SerialBuffer::reset()
{ {
m_head = 0U; m_head = 0U;
@ -74,8 +103,9 @@ void SerialBuffer::reset()
m_full = false; m_full = false;
} }
/* Helper to reset and reinitialize data values to defaults. */ /// <summary>
/// Helper to reset and reinitialize data values to defaults.
/// </summary>
void SerialBuffer::reinitialize(uint16_t length) void SerialBuffer::reinitialize(uint16_t length)
{ {
reset(); reset();
@ -86,8 +116,11 @@ void SerialBuffer::reinitialize(uint16_t length)
m_buffer = new uint8_t[length]; m_buffer = new uint8_t[length];
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
bool SerialBuffer::put(uint8_t c) bool SerialBuffer::put(uint8_t c)
{ {
if (m_full) if (m_full)
@ -105,15 +138,19 @@ bool SerialBuffer::put(uint8_t c)
return true; return true;
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint8_t SerialBuffer::peek() const uint8_t SerialBuffer::peek() const
{ {
return m_buffer[m_tail]; return m_buffer[m_tail];
} }
/* */ /// <summary>
///
/// </summary>
/// <returns></returns>
uint8_t SerialBuffer::get() uint8_t SerialBuffer::get()
{ {
uint8_t value = m_buffer[m_tail]; uint8_t value = m_buffer[m_tail];

@ -1,20 +1,36 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin, KI6ZUM
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file SerialBuffer.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file SerialBuffer.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Serial FIFO Control Copyright (C) 2015 by James McLaughlin KI6ZUM
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* 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(__SERIAL_RB_H__) #if !defined(__SERIAL_RB_H__)
#define __SERIAL_RB_H__ #define __SERIAL_RB_H__
@ -35,64 +51,34 @@ const uint16_t SERIAL_RINGBUFFER_SIZE = 396U;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// // Implements a circular buffer for serial data.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements a circular ring buffer for serial data.
* @ingroup hotspot_fw
*/
class DSP_FW_API SerialBuffer { class DSP_FW_API SerialBuffer {
public: public:
/** /// <summary>Initializes a new instance of the SerialBuffer class.</summary>
* @brief Initializes a new instance of the SerialBuffer class.
* @param length Length of buffer.
*/
SerialBuffer(uint16_t length = SERIAL_RINGBUFFER_SIZE); SerialBuffer(uint16_t length = SERIAL_RINGBUFFER_SIZE);
/** /// <summary>Finalizes a instance of the SerialBuffer class.</summary>
* @brief Finalizes a instance of the SerialBuffer class.
*/
~SerialBuffer(); ~SerialBuffer();
/** /// <summary>Helper to get how much space the ring buffer has for samples.</summary>
* @brief Helper to get how much space the ring buffer has for samples.
* @returns uint16_t Amount of space remaining for data.
*/
uint16_t getSpace() const; uint16_t getSpace() const;
/** /// <summary></summary>
* @brief
* @returns uint16_t
*/
uint16_t getData() const; uint16_t getData() const;
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Helper to reset and reinitialize data values to defaults.</summary>
* @brief Helper to reset and reinitialize data values to defaults.
* @param length Length of buffer.
*/
void reinitialize(uint16_t length); void reinitialize(uint16_t length);
/** /// <summary></summary>
* @brief
* @param c
* @returns bool
*/
bool put(uint8_t c); bool put(uint8_t c);
/** /// <summary></summary>
* @brief
* @returns uint8_t
*/
uint8_t peek() const; uint8_t peek() const;
/** /// <summary></summary>
* @brief
* @returns uint8_t
*/
uint8_t get(); uint8_t get();
private: private:

File diff suppressed because it is too large Load Diff

@ -1,22 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2018,2020,2021 Jonathan Naylor, G4KLX
* Copyright (C) 2018 Andy Uribe, CA6JAU
* Copyright (C) 2018,2021-2024 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file SerialPort.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file SerialPort.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
* @file SerialSTM.cpp * @package DVM / DSP Firmware (Hotspot)
* @ingroup hotspot_fw *
*/ */
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2018,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2018,2021-2022 Bryan Biedenkapp N2PLL
*
* 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(__SERIAL_PORT_H__) #if !defined(__SERIAL_PORT_H__)
#define __SERIAL_PORT_H__ #define __SERIAL_PORT_H__
@ -28,371 +40,209 @@
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @addtogroup modem
* @{
*/
/**
* @brief Modem operation states.
*/
enum DVM_STATE { enum DVM_STATE {
STATE_IDLE = 0U, //! Idle STATE_IDLE = 0U,
// DMR // DMR
STATE_DMR = 1U, //! DMR STATE_DMR = 1U,
// Project 25 // Project 25
STATE_P25 = 2U, //! Project 25 STATE_P25 = 2U,
// NXDN // NXDN
STATE_NXDN = 3U, //! NXDN STATE_NXDN = 3U,
// CW // CW
STATE_CW = 10U, //! Continuous Wave STATE_CW = 10U,
// Calibration States // Calibration States
STATE_INT_CAL = 90U, //! STATE_INT_CAL = 90U,
STATE_P25_CAL_1K = 92U, //! Project 25 Calibration 1K STATE_P25_CAL_1K = 92U,
STATE_DMR_DMO_CAL_1K = 93U, //! DMR DMO Calibration 1K STATE_DMR_DMO_CAL_1K = 93U,
STATE_DMR_CAL_1K = 94U, //! DMR Calibration 1K STATE_DMR_CAL_1K = 94U,
STATE_DMR_LF_CAL = 95U, //! DMR Low Frequency Calibration STATE_DMR_LF_CAL = 95U,
STATE_RSSI_CAL = 96U, //! RSSI Calibration STATE_RSSI_CAL = 96U,
STATE_P25_CAL = 97U, //! Project 25 Calibration STATE_P25_CAL = 97U,
STATE_DMR_CAL = 98U, //! DMR Calibration STATE_DMR_CAL = 98U,
STATE_NXDN_CAL = 99U //! NXDN Calibration STATE_NXDN_CAL = 99U
}; };
/**
* @brief Modem commands.
*/
enum DVM_COMMANDS { enum DVM_COMMANDS {
CMD_GET_VERSION = 0x00U, //! Get Modem Version CMD_GET_VERSION = 0x00U,
CMD_GET_STATUS = 0x01U, //! Get Modem Status CMD_GET_STATUS = 0x01U,
CMD_SET_CONFIG = 0x02U, //! Set Modem Configuration CMD_SET_CONFIG = 0x02U,
CMD_SET_MODE = 0x03U, //! Set Modem Mode CMD_SET_MODE = 0x03U,
CMD_SET_SYMLVLADJ = 0x04U, //! Set Symbol Level Adjustments CMD_SET_SYMLVLADJ = 0x04U,
CMD_SET_RXLEVEL = 0x05U, //! Set Rx Level CMD_SET_RXLEVEL = 0x05U,
CMD_SET_RFPARAMS = 0x06U, //! (Hotspot) Set RF Parameters CMD_SET_RFPARAMS = 0x06U,
CMD_CAL_DATA = 0x08U, //! Calibration Data CMD_CAL_DATA = 0x08U,
CMD_RSSI_DATA = 0x09U, //! RSSI Data CMD_RSSI_DATA = 0x09U,
CMD_SEND_CWID = 0x0AU, //! Send Continous Wave ID (Morse) CMD_SEND_CWID = 0x0AU,
CMD_SET_BUFFERS = 0x0FU, //! Set FIFO Buffer Lengths CMD_DMR_DATA1 = 0x18U,
CMD_DMR_LOST1 = 0x19U,
CMD_DMR_DATA1 = 0x18U, //! DMR Data Slot 1 CMD_DMR_DATA2 = 0x1AU,
CMD_DMR_LOST1 = 0x19U, //! DMR Data Lost Slot 1 CMD_DMR_LOST2 = 0x1BU,
CMD_DMR_DATA2 = 0x1AU, //! DMR Data Slot 2 CMD_DMR_SHORTLC = 0x1CU,
CMD_DMR_LOST2 = 0x1BU, //! DMR Data Lost Slot 2 CMD_DMR_START = 0x1DU,
CMD_DMR_SHORTLC = 0x1CU, //! DMR Short Link Control CMD_DMR_ABORT = 0x1EU,
CMD_DMR_START = 0x1DU, //! DMR Start Transmit CMD_DMR_CACH_AT_CTRL = 0x1FU,
CMD_DMR_ABORT = 0x1EU, //! DMR Abort
CMD_DMR_CACH_AT_CTRL = 0x1FU, //! DMR Set CACH AT Control CMD_P25_DATA = 0x31U,
CMD_DMR_CLEAR1 = 0x20U, //! DMR Clear Slot 1 Buffer CMD_P25_LOST = 0x32U,
CMD_DMR_CLEAR2 = 0x21U, //! DMR Clear Slot 2 Buffer CMD_P25_CLEAR = 0x33U,
CMD_P25_DATA = 0x31U, //! Project 25 Data CMD_NXDN_DATA = 0x41U,
CMD_P25_LOST = 0x32U, //! Project 25 Data Lost CMD_NXDN_LOST = 0x42U,
CMD_P25_CLEAR = 0x33U, //! Project 25 Clear Buffer
CMD_ACK = 0x70U,
CMD_NXDN_DATA = 0x41U, //! NXDN Data CMD_NAK = 0x7FU,
CMD_NXDN_LOST = 0x42U, //! NXDN Data Lost
CMD_NXDN_CLEAR = 0x43U, //! NXDN Clear Buffer CMD_FLSH_READ = 0xE0U,
CMD_FLSH_WRITE = 0xE1U,
CMD_ACK = 0x70U, //! Command ACK
CMD_NAK = 0x7FU, //! Command NACK CMD_DEBUG1 = 0xF1U,
CMD_DEBUG2 = 0xF2U,
CMD_FLSH_READ = 0xE0U, //! Read Flash Partition CMD_DEBUG3 = 0xF3U,
CMD_FLSH_WRITE = 0xE1U, //! Write Flash Partition CMD_DEBUG4 = 0xF4U,
CMD_DEBUG5 = 0xF5U,
CMD_RESET_MCU = 0xEAU, //! Soft Reboot MCU CMD_DEBUG_DUMP = 0xFAU,
CMD_DEBUG1 = 0xF1U, //!
CMD_DEBUG2 = 0xF2U, //!
CMD_DEBUG3 = 0xF3U, //!
CMD_DEBUG4 = 0xF4U, //!
CMD_DEBUG5 = 0xF5U, //!
CMD_DEBUG_DUMP = 0xFAU, //!
}; };
/**
* @brief Modem response reason codes.
*/
enum CMD_REASON_CODE { enum CMD_REASON_CODE {
RSN_OK = 0U, //! OK RSN_OK = 0U,
RSN_NAK = 1U, //! Negative Acknowledge RSN_NAK = 1U,
RSN_ILLEGAL_LENGTH = 2U, //! Illegal Length RSN_ILLEGAL_LENGTH = 2U,
RSN_INVALID_REQUEST = 4U, //! Invalid Request RSN_INVALID_REQUEST = 4U,
RSN_RINGBUFF_FULL = 8U, //! Ring Buffer Full RSN_RINGBUFF_FULL = 8U,
RSN_INVALID_FDMA_PREAMBLE = 10U, //! Invalid FDMA Preamble Length RSN_INVALID_FDMA_PREAMBLE = 10U,
RSN_INVALID_MODE = 11U, //! Invalid Mode RSN_INVALID_MODE = 11U,
RSN_INVALID_DMR_CC = 12U, //! Invalid DMR CC RSN_INVALID_DMR_CC = 12U,
RSN_INVALID_DMR_SLOT = 13U, //! Invalid DMR Slot RSN_INVALID_DMR_SLOT = 13U,
RSN_INVALID_DMR_START = 14U, //! Invaild DMR Start Transmit RSN_INVALID_DMR_START = 14U,
RSN_INVALID_DMR_RX_DELAY = 15U, //! Invalid DMR Rx Delay RSN_INVALID_DMR_RX_DELAY = 15U,
RSN_INVALID_P25_CORR_COUNT = 16U, //! Invalid P25 Correlation Count RSN_INVALID_P25_CORR_COUNT = 16U,
RSN_NO_INTERNAL_FLASH = 20U, //! No Internal Flash RSN_NO_INTERNAL_FLASH = 20U,
RSN_FAILED_ERASE_FLASH = 21U, //! Failed to erase flash partition RSN_FAILED_ERASE_FLASH = 21U,
RSN_FAILED_WRITE_FLASH = 22U, //! Failed to write flash partition RSN_FAILED_WRITE_FLASH = 22U,
RSN_FLASH_WRITE_TOO_BIG = 23U, //! Data to large for flash partition RSN_FLASH_WRITE_TOO_BIG = 23U,
RSN_HS_NO_DUAL_MODE = 32U, //! (Hotspot) No Dual Mode Operation RSN_HS_NO_DUAL_MODE = 32U,
RSN_DMR_DISABLED = 63U, //! DMR Disabled RSN_DMR_DISABLED = 63U,
RSN_P25_DISABLED = 64U, //! Project 25 Disabled RSN_P25_DISABLED = 64U,
RSN_NXDN_DISABLED = 65U //! NXDN Disabled RSN_NXDN_DISABLED = 65U
}; };
const uint8_t DVM_SHORT_FRAME_START = 0xFEU; const uint8_t DVM_FRAME_START = 0xFEU;
const uint8_t DVM_LONG_FRAME_START = 0xFDU;
#define SERIAL_FB_LEN 518U
#define SERIAL_SPEED 115200 #define SERIAL_SPEED 115200
/** @} */ #define STATE_SCAN_MAX 3
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements the RS232 serial bus to communicate with the HOST S/W.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements the RS232 serial bus to communicate with the HOST S/W.
* @ingroup hotspot_fw
*/
class DSP_FW_API SerialPort { class DSP_FW_API SerialPort {
public: public:
/** /// <summary>Initializes a new instance of the SerialPort class.</summary>
* @brief Initializes a new instance of the SerialPort class.
*/
SerialPort(); SerialPort();
/** /// <summary>Starts serial port communications.</summary>
* @brief Starts serial port communications.
*/
void start(); void start();
/** /// <summary>Process data from serial port.</summary>
* @brief Process data from serial port.
*/
void process(); void process();
/** /// <summary>Helper to check if the modem is in a calibration state.</summary>
* @brief Helper to check if the modem is in a calibration state.
* @param state
* @returns bool
*/
bool isCalState(DVM_STATE state); bool isCalState(DVM_STATE state);
/** /// <summary>Helper to determine digital mode if the modem is in a calibration state.</summary>
* @brief Helper to determine digital mode if the modem is in a calibration state.
* @param state
* @returns DVM_STATE
*/
DVM_STATE calRelativeState(DVM_STATE state); DVM_STATE calRelativeState(DVM_STATE state);
/** /// <summary>Write DMR frame data to serial port.</summary>
* @brief Write DMR frame data to serial port.
* @param slot DMR slot number.
* @param[in] data Data to write.
* @param length Length of data to write.
*/
void writeDMRData(bool slot, const uint8_t* data, uint8_t length); void writeDMRData(bool slot, const uint8_t* data, uint8_t length);
/** /// <summary>Write lost DMR frame data to serial port.</summary>
* @brief Write lost DMR frame data to serial port.
* @param slot DMR slot number.
*/
void writeDMRLost(bool slot); void writeDMRLost(bool slot);
/** /// <summary>Write P25 frame data to serial port.</summary>
* @brief Write P25 frame data to serial port. void writeP25Data(const uint8_t* data, uint8_t length);
* @param[in] data Data to write. /// <summary>Write lost P25 frame data to serial port.</summary>
* @param length Length of data to write.
*/
void writeP25Data(const uint8_t* data, uint16_t length);
/**
* @brief Write lost P25 frame data to serial port.
*/
void writeP25Lost(); void writeP25Lost();
/** /// <summary>Write NXDN frame data to serial port.</summary>
* @brief Write NXDN frame data to serial port.
* @param[in] data Data to write.
* @param length Length of data to write.
*/
void writeNXDNData(const uint8_t* data, uint8_t length); void writeNXDNData(const uint8_t* data, uint8_t length);
/** /// <summary>Write lost NXDN frame data to serial port.</summary>
* @brief Write lost NXDN frame data to serial port.
*/
void writeNXDNLost(); void writeNXDNLost();
/** /// <summary>Write calibration frame data to serial port.</summary>
* @brief Write calibration frame data to serial port.
* @param[in] data Data to write.
* @param length Length of data to write.
*/
void writeCalData(const uint8_t* data, uint8_t length); void writeCalData(const uint8_t* data, uint8_t length);
/** /// <summary>Write RSSI frame data to serial port.</summary>
* @brief Write RSSI frame data to serial port.
* @param[in] data Data to write.
* @param length Length of data to write.
*/
void writeRSSIData(const uint8_t* data, uint8_t length); void writeRSSIData(const uint8_t* data, uint8_t length);
/** /// <summary></summary>
* @brief
* @param[in] text
*/
void writeDebug(const char* text); void writeDebug(const char* text);
/** /// <summary></summary>
* @brief
* @param[in] text
* @param n1
*/
void writeDebug(const char* text, int16_t n1); void writeDebug(const char* text, int16_t n1);
/** /// <summary></summary>
* @brief
* @param[in] text
* @param n1
* @param n2
*/
void writeDebug(const char* text, int16_t n1, int16_t n2); void writeDebug(const char* text, int16_t n1, int16_t n2);
/** /// <summary></summary>
* @brief
* @param[in] text
* @param n1
* @param n2
* @param n3
*/
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3); void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3);
/** /// <summary></summary>
* @brief
* @param[in] text
* @param n1
* @param n2
* @param n3
* @param n4
*/
void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4); void writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4);
/** /// <summary></summary>
* @brief
* @param[in] data
* @param length
*/
void writeDump(const uint8_t* data, uint16_t length); void writeDump(const uint8_t* data, uint16_t length);
private: private:
uint8_t m_buffer[SERIAL_FB_LEN]; uint8_t m_buffer[256U];
uint16_t m_ptr; uint8_t m_ptr;
uint16_t m_len; uint8_t m_len;
bool m_dblFrame;
bool m_debug; bool m_debug;
/** /// <summary>Write acknowlegement.</summary>
* @brief Write acknowlegement.
*/
void sendACK(); void sendACK();
/** /// <summary>Write negative acknowlegement.</summary>
* @brief Write negative acknowlegement.
* @param err
*/
void sendNAK(uint8_t err); void sendNAK(uint8_t err);
/** /// <summary>Write modem DSP status.</summary>
* @brief Write modem DSP status.
*/
void getStatus(); void getStatus();
/** /// <summary>Write modem DSP version.</summary>
* @brief Write modem DSP version.
*/
void getVersion(); void getVersion();
/** /// <summary>Helper to validate the passed modem state is valid.</summary>
* @brief Helper to validate the passed modem state is valid.
* @param state
* @returns uint8_t Reason code.
*/
uint8_t modemStateCheck(DVM_STATE state); uint8_t modemStateCheck(DVM_STATE state);
/** /// <summary>Set modem DSP configuration from serial port data.</summary>
* @brief Set modem DSP configuration from serial port data.
* @param[in] data Buffer containing configuration frame.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t setConfig(const uint8_t* data, uint8_t length); uint8_t setConfig(const uint8_t* data, uint8_t length);
/** /// <summary>Set modem DSP mode from serial port data.</summary>
* @brief Set modem DSP mode from serial port data.
* @param[in] data Buffer containing mode frame.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t setMode(const uint8_t* data, uint8_t length); uint8_t setMode(const uint8_t* data, uint8_t length);
/** /// <summary>Sets the modem state.</summary>
* @brief Sets the modem state.
* @param modemState
*/
void setMode(DVM_STATE modemState); void setMode(DVM_STATE modemState);
/** /// <summary>Sets the RF parameters.</summary>
* @brief Sets the RF parameters.
* @param[in] data Buffer containing RF parameters frame.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t setRFParams(const uint8_t* data, uint8_t length); uint8_t setRFParams(const uint8_t* data, uint8_t length);
/**
* @brief Sets the protocol ring buffer sizes. /// <summary></summary>
* @param[in] data Buffer containing set buffers frame.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t setBuffers(const uint8_t* data, uint8_t length);
/**
* @brief Reads data from the modem flash parititon.
*/
void flashRead(); void flashRead();
/** /// <summary></summary>
* @brief Writes data to the modem flash partition.
* @param[in] data Buffer containing data to write to flash partition.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t flashWrite(const uint8_t* data, uint8_t length); uint8_t flashWrite(const uint8_t* data, uint8_t length);
// Hardware specific routines // Hardware specific routines
/** /// <summary></summary>
* @brief
* @param n
* @param speed
*/
void beginInt(uint8_t n, int speed); void beginInt(uint8_t n, int speed);
/** /// <summary></summary>
* @brief
* @param n
*/
int availableInt(uint8_t n); int availableInt(uint8_t n);
/** /// <summary></summary>
* @brief
* @param n
*/
int availableForWriteInt(uint8_t n); int availableForWriteInt(uint8_t n);
/** /// <summary></summary>
* @brief
* @param n
*/
uint8_t readInt(uint8_t n); uint8_t readInt(uint8_t n);
/** /// <summary></summary>
* @brief
* @param n
* @param[in] data
* @param length
* @param flush
*/
void writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush = false); void writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush = false);
}; };

@ -1,20 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016 by Jim McLaughlin KI6ZUM
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021-2022 Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2016 Jim McLaughlin, KI6ZUM * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017,2018,2019 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021,2022,2024 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "SerialPort.h" #include "SerialPort.h"
#include "STM_UART.h" #include "STM_UART.h"
#if defined(STM32F10X_MD) #if defined(STM32F10X_MD)
#include <stm32f10x_flash.h> #include <stm32f10x_flash.h>
#include <usb_serial.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
@ -50,18 +71,18 @@ extern "C" {
static STM_UART m_USART1; static STM_UART m_USART1;
/** /// <summary>
* @brief Helper to handle the USART1 IRQ. ///
*/ /// </summary>
void USART1_IRQHandler() void USART1_IRQHandler()
{ {
m_USART1.handleIRQ(); m_USART1.handleIRQ();
} }
/** /// <summary>
* @brief Initializes the USART1. ///
* @param speed Port speed. /// </summary>
*/ /// <param name="speed"></param>
void InitUSART1(int speed) void InitUSART1(int speed)
{ {
// USART1 - TXD PA9 - RXD PA10 // USART1 - TXD PA9 - RXD PA10
@ -115,18 +136,18 @@ void InitUSART1(int speed)
static STM_UART m_USART2; static STM_UART m_USART2;
/** /// <summary>
* @brief Helper to handle the USART5 IRQ. ///
*/ /// </summary>
void USART2_IRQHandler() void USART2_IRQHandler()
{ {
m_USART2.handleIRQ(); m_USART2.handleIRQ();
} }
/** /// <summary>
* @brief Initializes the USART5. ///
* @param speed Port speed. /// </summary>
*/ /// <param name="speed"></param>
void InitUSART2(int speed) void InitUSART2(int speed)
{ {
// USART2 - TXD PA2 - RXD PA3 // USART2 - TXD PA2 - RXD PA3
@ -176,13 +197,14 @@ void InitUSART2(int speed)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Reads data from the modem flash parititon. */ /// <summary>
///
/// </summary>
void SerialPort::flashRead() void SerialPort::flashRead()
{ {
uint8_t reply[249U]; uint8_t reply[249U];
reply[0U] = DVM_SHORT_FRAME_START; reply[0U] = DVM_FRAME_START;
reply[1U] = 249U; reply[1U] = 249U;
reply[2U] = CMD_FLSH_READ; reply[2U] = CMD_FLSH_READ;
@ -191,8 +213,11 @@ void SerialPort::flashRead()
writeInt(1U, reply, 249U); writeInt(1U, reply, 249U);
} }
/* Writes data to the modem flash partition. */ /// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
uint8_t SerialPort::flashWrite(const uint8_t* data, uint8_t length) uint8_t SerialPort::flashWrite(const uint8_t* data, uint8_t length)
{ {
if (length > 249U) { if (length > 249U) {
@ -238,13 +263,16 @@ uint8_t SerialPort::flashWrite(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <param name="speed"></param>
void SerialPort::beginInt(uint8_t n, int speed) void SerialPort::beginInt(uint8_t n, int speed)
{ {
switch (n) { switch (n) {
case 1U: case 1U:
InitUSART1(speed); usbserial.begin();
break; break;
case 3U: case 3U:
InitUSART2(speed); InitUSART2(speed);
@ -254,13 +282,16 @@ void SerialPort::beginInt(uint8_t n, int speed)
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
int SerialPort::availableInt(uint8_t n) int SerialPort::availableInt(uint8_t n)
{ {
switch (n) { switch (n) {
case 1U: case 1U:
return m_USART1.available(); return usbserial.available();
case 3U: case 3U:
m_USART2.available(); m_USART2.available();
default: default:
@ -268,13 +299,17 @@ int SerialPort::availableInt(uint8_t n)
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
int SerialPort::availableForWriteInt(uint8_t n) int SerialPort::availableForWriteInt(uint8_t n)
{ {
switch (n) { switch (n) {
case 1U: case 1U:
return m_USART1.availableForWrite(); //return usbserial.availableForWrite();
return 1U; // we don't have this -- so fake it
case 3U: case 3U:
return m_USART2.availableForWrite(); return m_USART2.availableForWrite();
default: default:
@ -282,13 +317,16 @@ int SerialPort::availableForWriteInt(uint8_t n)
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
uint8_t SerialPort::readInt(uint8_t n) uint8_t SerialPort::readInt(uint8_t n)
{ {
switch (n) { switch (n) {
case 1U: case 1U:
return m_USART1.read(); return usbserial.read();
case 3U: case 3U:
return m_USART2.read(); return m_USART2.read();
default: default:
@ -297,15 +335,20 @@ uint8_t SerialPort::readInt(uint8_t n)
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <param name="data"></param>
/// <param name="length"></param>
/// <param name="flush"></param>
void SerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush) void SerialPort::writeInt(uint8_t n, const uint8_t* data, uint16_t length, bool flush)
{ {
switch (n) { switch (n) {
case 1U: case 1U:
m_USART1.write(data, length); usbserial.write(data, length);
if (flush) if (flush)
m_USART1.flush(); usbserial.flush();
break; break;
case 3U: case 3U:
m_USART2.write(data, length); m_USART2.write(data, length);

@ -1,13 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2017 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2015,2020 Jonathan Naylor, G4KLX * it under the terms of the GNU General Public License as published by
* Copyright (C) 2017 Andy Uribe, CA6JAU * 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 "Utils.h" #include "Utils.h"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -25,15 +45,11 @@ const uint8_t BITS_TABLE[] = {
// Global Functions // Global Functions
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Returns the count of bits in the passed 8 byte value. */
uint8_t countBits8(uint8_t bits) uint8_t countBits8(uint8_t bits)
{ {
return BITS_TABLE[bits]; return BITS_TABLE[bits];
} }
/* Returns the count of bits in the passed 16 byte value. */
uint8_t countBits16(uint16_t bits) uint8_t countBits16(uint16_t bits)
{ {
uint8_t* p = (uint8_t*)&bits; uint8_t* p = (uint8_t*)&bits;
@ -43,8 +59,6 @@ uint8_t countBits16(uint16_t bits)
return n; return n;
} }
/* Returns the count of bits in the passed 32 byte value. */
uint8_t countBits32(uint32_t bits) uint8_t countBits32(uint32_t bits)
{ {
uint8_t* p = (uint8_t*)&bits; uint8_t* p = (uint8_t*)&bits;
@ -56,8 +70,6 @@ uint8_t countBits32(uint32_t bits)
return n; return n;
} }
/* Returns the count of bits in the passed 64 byte value. */
uint8_t countBits64(uint64_t bits) uint8_t countBits64(uint64_t bits)
{ {
uint8_t* p = (uint8_t*)&bits; uint8_t* p = (uint8_t*)&bits;

@ -1,19 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2020 Jonathan Naylor, G4KLX
* Copyright (C) 2017 Andy Uribe, CA6JAU
*
*/
/** /**
* @file Utils.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup hotspot_fw * GPLv2 Open Source. Use is subject to license terms.
* @file Utils.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup hotspot_fw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
*
* 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(__UTILS_H__) #if !defined(__UTILS_H__)
#define __UTILS_H__ #define __UTILS_H__
@ -31,29 +45,9 @@
// Global Functions // Global Functions
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Returns the count of bits in the passed 8 byte value.
* @param bits uint8_t to count bits for.
* @returns uint8_t Count of bits in passed value.
*/
DSP_FW_API uint8_t countBits8(uint8_t bits); DSP_FW_API uint8_t countBits8(uint8_t bits);
/**
* @brief Returns the count of bits in the passed 16 byte value.
* @param bits uint16_t to count bits for.
* @returns uint8_t Count of bits in passed value.
*/
DSP_FW_API uint8_t countBits16(uint16_t bits); DSP_FW_API uint8_t countBits16(uint16_t bits);
/**
* @brief Returns the count of bits in the passed 32 byte value.
* @param bits uint32_t to count bits for.
* @returns uint8_t Count of bits in passed value.
*/
DSP_FW_API uint8_t countBits32(uint32_t bits); DSP_FW_API uint8_t countBits32(uint32_t bits);
/**
* @brief Returns the count of bits in the passed 64 byte value.
* @param bits ulong64_t to count bits for.
* @returns uint8_t Count of bits in passed value.
*/
DSP_FW_API uint8_t countBits64(ulong64_t bits); DSP_FW_API uint8_t countBits64(ulong64_t bits);
#endif // __UTILS_H__ #endif // __UTILS_H__

@ -1,19 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Colin Durbridge G4EML
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2018,2019 by Andy Uribe CA6JAU
* *
* Copyright (C) 2009-2015 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016 Colin Durbridge, G4EML * it under the terms of the GNU General Public License as published by
* Copyright (C) 2018,2019 Andy Uribe, CA6JAU * 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 "Globals.h" #include "Globals.h"
#include "dmr/CalDMR.h" #include "dmr/CalDMR.h"
using namespace dmr; using namespace dmr;
#if defined(ENABLE_DMR)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -75,8 +97,9 @@ const uint8_t SHORTLC_1K[] = { 0x33U, 0x3AU, 0xA0U, 0x30U, 0x00U, 0x55U, 0xA6U,
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the CalDMR class. */ /// <summary>
/// Initializes a new instance of the CalDMR class.
/// </summary>
CalDMR::CalDMR() : CalDMR::CalDMR() :
m_transmit(false), m_transmit(false),
m_state(DMRCAL1K_IDLE), m_state(DMRCAL1K_IDLE),
@ -88,8 +111,9 @@ CalDMR::CalDMR() :
::memcpy(m_dmr1k, VOICE_1K, DMR_FRAME_LENGTH_BYTES + 1U); ::memcpy(m_dmr1k, VOICE_1K, DMR_FRAME_LENGTH_BYTES + 1U);
} }
/* Process local state and transmit on the air interface. */ /// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalDMR::process() void CalDMR::process()
{ {
switch (m_modemState) { switch (m_modemState) {
@ -127,8 +151,10 @@ void CalDMR::process()
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
void CalDMR::createData1k(uint8_t n) void CalDMR::createData1k(uint8_t n)
{ {
for (uint8_t i = 0; i < 5U; i++) for (uint8_t i = 0; i < 5U; i++)
@ -140,8 +166,10 @@ void CalDMR::createData1k(uint8_t n)
m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U; m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U;
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="n"></param>
void CalDMR::createDataDMO1k(uint8_t n) void CalDMR::createDataDMO1k(uint8_t n)
{ {
for (uint8_t i = 0; i < 5U; i++) for (uint8_t i = 0; i < 5U; i++)
@ -153,8 +181,9 @@ void CalDMR::createDataDMO1k(uint8_t n)
m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U; m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U;
} }
/* */ /// <summary>
///
/// </summary>
void CalDMR::dmr1kcal() void CalDMR::dmr1kcal()
{ {
#if defined(DUPLEX) #if defined(DUPLEX)
@ -203,8 +232,9 @@ void CalDMR::dmr1kcal()
#endif #endif
} }
/* */ /// <summary>
///
/// </summary>
void CalDMR::dmrDMO1kcal() void CalDMR::dmrDMO1kcal()
{ {
dmrDMOTX.process(); dmrDMOTX.process();
@ -240,8 +270,12 @@ void CalDMR::dmrDMO1kcal()
} }
} }
/* Write DMR calibration state. */ /// <summary>
/// Write DMR calibration state.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalDMR::write(const uint8_t* data, uint8_t length) uint8_t CalDMR::write(const uint8_t* data, uint8_t length)
{ {
if (length != 1U) if (length != 1U)
@ -257,3 +291,5 @@ uint8_t CalDMR::write(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
#endif // defined(ENABLE_DMR)

@ -1,95 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2009-2015 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Colin Durbridge, G4EML
* Copyright (C) 2018 Andy Uribe, CA6JAU
*
*/
/** /**
* @file CalDMR.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file CalDMR.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2015 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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(__CAL_DMR_H__) #if !defined(__CAL_DMR_H__)
#define __CAL_DMR_H__ #define __CAL_DMR_H__
#include "Defines.h" #include "Defines.h"
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#if defined(ENABLE_DMR)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** enum DMRCAL1K {
* @brief Calibration States DMRCAL1K_IDLE,
* @ingroup dmr_mfw DMRCAL1K_VH,
*/ DMRCAL1K_VOICE,
enum DMR1KCAL { DMRCAL1K_VT,
DMRCAL1K_IDLE, //! Idle DMRCAL1K_WAIT
DMRCAL1K_VH, //! Voice Header
DMRCAL1K_VOICE, //! Voice
DMRCAL1K_VT, //! Voice Terminator
DMRCAL1K_WAIT //!
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements logic for DMR calibration mode.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements logic for DMR calibration mode.
* @ingroup dmr_hfw
*/
class DSP_FW_API CalDMR { class DSP_FW_API CalDMR {
public: public:
/** /// <summary>Initializes a new instance of the CalDMR class.</summary>
* @brief Initializes a new instance of the CalDMR class.
*/
CalDMR(); CalDMR();
/** /// <summary>Process local state and transmit on the air interface.</summary>
* @brief Process local state and transmit on the air interface.
*/
void process(); void process();
/** /// <summary></summary>
* @brief
* @param n
*/
void createData1k(uint8_t n); void createData1k(uint8_t n);
/** /// <summary></summary>
* @brief
* @param n
*/
void createDataDMO1k(uint8_t n); void createDataDMO1k(uint8_t n);
/** /// <summary></summary>
* @brief
*/
void dmr1kcal(); void dmr1kcal();
/** /// <summary></summary>
* @brief
*/
void dmrDMO1kcal(); void dmrDMO1kcal();
/** /// <summary>Write DMR calibration state.</summary>
* @brief Write DMR calibration state.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t write(const uint8_t* data, uint8_t length); uint8_t write(const uint8_t* data, uint8_t length);
private: private:
bool m_transmit; bool m_transmit;
DMR1KCAL m_state; DMRCAL1K m_state;
uint32_t m_frameStart; uint32_t m_frameStart;
uint8_t m_dmr1k[DMR_FRAME_LENGTH_BYTES + 1U]; uint8_t m_dmr1k[DMR_FRAME_LENGTH_BYTES + 1U];
@ -99,4 +89,6 @@ namespace dmr
}; };
} // namespace dmr } // namespace dmr
#endif // defined(ENABLE_DMR)
#endif // __CAL_DMR_H__ #endif // __CAL_DMR_H__

@ -1,14 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021 by Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2009-2016 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRDMORX.h" #include "dmr/DMRDMORX.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
@ -16,6 +36,8 @@
using namespace dmr; using namespace dmr;
#if defined(ENABLE_DMR)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -34,8 +56,9 @@ const uint8_t CONTROL_DATA = 0x40U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRDMORX class. */ /// <summary>
/// Initializes a new instance of the DMRDMORX class.
/// </summary>
DMRDMORX::DMRDMORX() : DMRDMORX::DMRDMORX() :
m_bitBuffer(0x00U), m_bitBuffer(0x00U),
m_buffer(), m_buffer(),
@ -53,8 +76,9 @@ DMRDMORX::DMRDMORX() :
/* stub */ /* stub */
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRDMORX::reset() void DMRDMORX::reset()
{ {
m_syncPtr = 0U; m_syncPtr = 0U;
@ -65,8 +89,10 @@ void DMRDMORX::reset()
m_endPtr = NOENDPTR; m_endPtr = NOENDPTR;
} }
/* Sample DMR bits from the air interface. */ /// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRDMORX::databit(bool bit) void DMRDMORX::databit(bool bit)
{ {
_WRITE_BIT(m_buffer, m_dataPtr, bit); _WRITE_BIT(m_buffer, m_dataPtr, bit);
@ -117,7 +143,7 @@ void DMRDMORX::databit(bool bit)
switch (dataType) { switch (dataType) {
case DT_DATA_HEADER: case DT_DATA_HEADER:
DEBUG2("DMRDMORX::databit() data header found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): data header found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMORXS_DATA; m_state = DMORXS_DATA;
m_type = 0x00U; m_type = 0x00U;
@ -126,32 +152,32 @@ void DMRDMORX::databit(bool bit)
case DT_RATE_34_DATA: case DT_RATE_34_DATA:
case DT_RATE_1_DATA: case DT_RATE_1_DATA:
if (m_state == DMORXS_DATA) { if (m_state == DMORXS_DATA) {
DEBUG2("DMRDMORX::databit() data payload found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): data payload found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_type = dataType; m_type = dataType;
} }
break; break;
case DT_VOICE_LC_HEADER: case DT_VOICE_LC_HEADER:
DEBUG2("DMRDMORX::databit() voice header found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): voice header found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMORXS_VOICE; m_state = DMORXS_VOICE;
break; break;
case DT_VOICE_PI_HEADER: case DT_VOICE_PI_HEADER:
if (m_state == DMORXS_VOICE) { if (m_state == DMORXS_VOICE) {
DEBUG2("DMRDMORX::databit() voice pi header found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): voice pi header found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
} }
m_state = DMORXS_VOICE; m_state = DMORXS_VOICE;
break; break;
case DT_TERMINATOR_WITH_LC: case DT_TERMINATOR_WITH_LC:
if (m_state == DMORXS_VOICE) { if (m_state == DMORXS_VOICE) {
DEBUG2("DMRDMORX::databit() voice terminator found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): voice terminator found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
reset(); reset();
} }
break; break;
default: // DT_CSBK default: // DT_CSBK
DEBUG2("DMRDMORX::databit() csbk found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): csbk found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
reset(); reset();
break; break;
@ -160,7 +186,7 @@ void DMRDMORX::databit(bool bit)
} }
else if (m_control == CONTROL_VOICE) { else if (m_control == CONTROL_VOICE) {
// Voice sync // Voice sync
DEBUG2("DMRDMORX::databit() voice sync found pos", m_syncPtr); DEBUG2("DMRDMORX: databit(): voice sync found pos", m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMORXS_VOICE; m_state = DMORXS_VOICE;
@ -171,7 +197,7 @@ void DMRDMORX::databit(bool bit)
if (m_state != DMORXS_NONE) { if (m_state != DMORXS_NONE) {
m_syncCount++; m_syncCount++;
if (m_syncCount >= MAX_SYNC_LOST_FRAMES) { if (m_syncCount >= MAX_SYNC_LOST_FRAMES) {
DEBUG1("DMRDMORX::databit() sync timeout, lost lock"); DEBUG1("DMRDMORX: databit(): sync timeout, lost lock");
serial.writeDMRLost(true); serial.writeDMRLost(true);
reset(); reset();
} }
@ -207,8 +233,10 @@ void DMRDMORX::databit(bool bit)
io.setDecode(m_state != DMORXS_NONE); io.setDecode(m_state != DMORXS_NONE);
} }
/* Sets the DMR color code. */ /// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRDMORX::setColorCode(uint8_t colorCode) void DMRDMORX::setColorCode(uint8_t colorCode)
{ {
m_colorCode = colorCode; m_colorCode = colorCode;
@ -218,8 +246,9 @@ void DMRDMORX::setColorCode(uint8_t colorCode)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Frame synchronization correlator. */ /// <summary>
/// Frame synchronization correlator.
/// </summary>
void DMRDMORX::correlateSync() void DMRDMORX::correlateSync()
{ {
// unpack sync bytes // unpack sync bytes
@ -238,11 +267,11 @@ void DMRDMORX::correlateSync()
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]); errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
DEBUG2("DMRDMORX::correlateSync() sync errs", errs); DEBUG2("DMRDMORX: correlateSync(): correlateSync errs", errs);
DEBUG4("DMRDMORX::correlateSync() sync [b0 - b2]", sync[0], sync[1], sync[2]); DEBUG4("DMRDMORX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
DEBUG4("DMRDMORX::correlateSync() sync [b3 - b5]", sync[3], sync[4], sync[5]); DEBUG4("DMRDMORX: correlateSync(): sync [b3 - b5]", sync[3], sync[4], sync[5]);
DEBUG2("DMRDMORX::correlateSync() sync [b6]", sync[6]); DEBUG2("DMRDMORX: correlateSync(): sync [b6]", sync[6]);
m_control = CONTROL_DATA; m_control = CONTROL_DATA;
m_syncPtr = m_dataPtr; m_syncPtr = m_dataPtr;
@ -255,18 +284,18 @@ void DMRDMORX::correlateSync()
if (m_endPtr >= DMO_BUFFER_LENGTH_BITS) if (m_endPtr >= DMO_BUFFER_LENGTH_BITS)
m_endPtr -= DMO_BUFFER_LENGTH_BITS; m_endPtr -= DMO_BUFFER_LENGTH_BITS;
DEBUG4("DMRDMORX::correlateSync() dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr); DEBUG4("DMRDMORX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
} else if ((countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) || } else if ((countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) ||
(countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_S2_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS)) { (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_S2_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS)) {
uint8_t errs = 0U; uint8_t errs = 0U;
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]); errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
DEBUG2("DMRDMORX::correlateSync() correlateSync errs", errs); DEBUG2("DMRDMORX: correlateSync(): correlateSync errs", errs);
DEBUG4("DMRDMORX::correlateSync() sync [b0 - b2]", sync[0], sync[1], sync[2]); DEBUG4("DMRDMORX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
DEBUG4("DMRDMORX::correlateSync() sync [b3 - b5]", sync[3], sync[4], sync[5]); DEBUG4("DMRDMORX: correlateSync(): sync [b3 - b5]", sync[3], sync[4], sync[5]);
DEBUG2("DMRDMORX::correlateSync() sync [b6]", sync[6]); DEBUG2("DMRDMORX: correlateSync(): sync [b6]", sync[6]);
m_control = CONTROL_VOICE; m_control = CONTROL_VOICE;
m_syncPtr = m_dataPtr; m_syncPtr = m_dataPtr;
@ -279,12 +308,16 @@ void DMRDMORX::correlateSync()
if (m_endPtr >= DMO_BUFFER_LENGTH_BITS) if (m_endPtr >= DMO_BUFFER_LENGTH_BITS)
m_endPtr -= DMO_BUFFER_LENGTH_BITS; m_endPtr -= DMO_BUFFER_LENGTH_BITS;
DEBUG4("DMRDMORX::correlateSync() dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr); DEBUG4("DMRDMORX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void DMRDMORX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer) void DMRDMORX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{ {
for (uint8_t i = 0U; i < count; i++) { for (uint8_t i = 0U; i < count; i++) {
@ -331,8 +364,10 @@ void DMRDMORX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="frame"></param>
void DMRDMORX::writeRSSIData(uint8_t* frame) void DMRDMORX::writeRSSIData(uint8_t* frame)
{ {
#if defined(SEND_RSSI_DATA) #if defined(SEND_RSSI_DATA)
@ -346,3 +381,5 @@ void DMRDMORX::writeRSSIData(uint8_t* frame)
serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U); serial.writeDMRData(true, frame, DMR_FRAME_LENGTH_BYTES + 1U);
#endif #endif
} }
#endif // defined(ENABLE_DMR)

@ -1,26 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file DMRDMORX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRDMORX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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(__DMR_DMO_RX_H__) #if !defined(__DMR_DMO_RX_H__)
#define __DMR_DMO_RX_H__ #define __DMR_DMO_RX_H__
#include "Defines.h" #include "Defines.h"
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#if defined(ENABLE_DMR)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -29,46 +45,29 @@ namespace dmr
const uint16_t DMO_BUFFER_LENGTH_BITS = 576U; const uint16_t DMO_BUFFER_LENGTH_BITS = 576U;
/**
* @brief DMR DMO Receiver State
* @ingroup dmr_hfw
*/
enum DMORX_STATE { enum DMORX_STATE {
DMORXS_NONE, //! None DMORXS_NONE,
DMORXS_VOICE, //! Voice Data DMORXS_VOICE,
DMORXS_DATA //! PDU Data DMORXS_DATA
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for DMR DMO mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for DMR DMO mode operation.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRDMORX { class DSP_FW_API DMRDMORX {
public: public:
/** /// <summary>Initializes a new instance of the DMRDMORX class.</summary>
* @brief Initializes a new instance of the DMRDMORX class.
*/
DMRDMORX(); DMRDMORX();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample DMR bits from the air interface.</summary>
* @brief Sample DMR bits from the air interface.
* @param bit
*/
void databit(bool bit); void databit(bool bit);
/** /// <summary>Sets the DMR color code.</summary>
* @brief Sets the DMR color code.
* @param colorCode
*/
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
private: private:
@ -93,24 +92,16 @@ namespace dmr
uint8_t m_type; uint8_t m_type;
/** /// <summary>Frame synchronization correlator.</summary>
* @brief Frame synchronization correlator.
*/
void correlateSync(); void correlateSync();
/** /// <summary></summary>
* @brief
* @param start
* @param count
* @param buffer
*/
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer); void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
/** /// <summary></summary>
* @brief
* @param frame
*/
void writeRSSIData(uint8_t* frame); void writeRSSIData(uint8_t* frame);
}; };
} // namespace dmr } // namespace dmr
#endif // defined(ENABLE_DMR)
#endif // __DMR_DMO_RX_H__ #endif // __DMR_DMO_RX_H__

@ -1,20 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Colin Durbridge G4EML
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* * Copyright (C) 2021 by Bryan Biedenkapp N2PLL
* Copyright (C) 2009-2016 Jonathan Naylor, G4KLX *
* Copyright (C) 2016 Colin Durbridge, G4EML * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
using namespace dmr; using namespace dmr;
#if defined(ENABLE_DMR)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -30,8 +52,9 @@ const uint8_t PR_FILL[] =
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRDMOTX class. */ /// <summary>
/// Initializes a new instance of the DMRDMOTX class.
/// </summary>
DMRDMOTX::DMRDMOTX() : DMRDMOTX::DMRDMOTX() :
m_fifo(DMR_TX_BUFFER_LEN), m_fifo(DMR_TX_BUFFER_LEN),
m_poBuffer(), m_poBuffer(),
@ -42,8 +65,9 @@ DMRDMOTX::DMRDMOTX() :
/* stub */ /* stub */
} }
/* Process local buffer and transmit on the air interface. */ /// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void DMRDMOTX::process() void DMRDMOTX::process()
{ {
if (m_poLen == 0U && m_fifo.getData() > 0U) { if (m_poLen == 0U && m_fifo.getData() > 0U) {
@ -63,6 +87,7 @@ void DMRDMOTX::process()
m_poLen = 72U; m_poLen = 72U;
} }
DEBUG2("DMRDMOTX: process(): poLen", m_poLen);
m_poPtr = 0U; m_poPtr = 0U;
} }
@ -85,15 +110,19 @@ void DMRDMOTX::process()
} }
} }
/* Write data to the local buffer. */ /// <summary>
/// Write data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRDMOTX::writeData(const uint8_t* data, uint8_t length) uint8_t DMRDMOTX::writeData(const uint8_t* data, uint8_t length)
{ {
if (length != (DMR_FRAME_LENGTH_BYTES + 1U)) if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace(); uint16_t space = m_fifo.getSpace();
DEBUG3("DMRDMOTX::writeData() dataLength/fifoLength", length, space); DEBUG3("DMRDMOTX: writeData(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES) if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL; return RSN_RINGBUFF_FULL;
@ -103,28 +132,24 @@ uint8_t DMRDMOTX::writeData(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
/* Sets the FDMA preamble count. */ /// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void DMRDMOTX::setPreambleCount(uint8_t preambleCnt) void DMRDMOTX::setPreambleCount(uint8_t preambleCnt)
{ {
uint32_t preambles = (uint32_t)((float)preambleCnt / 0.2083F); uint32_t preambles = (uint32_t)((float)preambleCnt / 0.2083F);
m_preambleCnt = DMRDMO_FIXED_DELAY + preambles; m_preambleCnt = DMRDMO_FIXED_DELAY + preambles;
// clamp preamble count to 16ms maximum // clamp preamble count to 250ms maximum
if (m_preambleCnt > 80U) if (m_preambleCnt > 1200U)
m_preambleCnt = 80U; m_preambleCnt = 1200U;
}
/* Helper to resize the FIFO buffer. */
void DMRDMOTX::resizeBuffer(uint16_t size)
{
m_fifo.reset();
m_fifo.reinitialize(size);
} }
/* Helper to get how much space the ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint16_t DMRDMOTX::getSpace() const uint16_t DMRDMOTX::getSpace() const
{ {
return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U); return m_fifo.getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
@ -134,8 +159,10 @@ uint16_t DMRDMOTX::getSpace() const
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to write a raw byte to the DAC. */ /// <summary>
///
/// </summary>
/// <param name="c"></param>
void DMRDMOTX::writeByte(uint8_t c) void DMRDMOTX::writeByte(uint8_t c)
{ {
uint8_t bit; uint8_t bit;
@ -150,3 +177,5 @@ void DMRDMOTX::writeByte(uint8_t c)
io.write(&bit, 1); io.write(&bit, 1);
} }
} }
#endif // defined(ENABLE_DMR)

@ -1,21 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Colin Durbridge, G4EML
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file DMRDMOTX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRDMOTX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
*
* 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(__DMR_DMO_TX_H__) #if !defined(__DMR_DMO_TX_H__)
#define __DMR_DMO_TX_H__ #define __DMR_DMO_TX_H__
@ -23,6 +37,8 @@
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#include "SerialBuffer.h" #include "SerialBuffer.h"
#if defined(ENABLE_DMR)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -34,48 +50,24 @@ namespace dmr
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements transmitter logic for DMR DMO mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements transmitter logic for DMR DMO mode operation.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRDMOTX { class DSP_FW_API DMRDMOTX {
public: public:
/** /// <summary>Initializes a new instance of the DMRDMOTX class.</summary>
* @brief Initializes a new instance of the DMRDMOTX class.
*/
DMRDMOTX(); DMRDMOTX();
/** /// <summary>Process local buffer and transmit on the air interface.</summary>
* @brief Process local buffer and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write data to the local buffer.</summary>
* @brief Write data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeData(const uint8_t* data, uint8_t length); uint8_t writeData(const uint8_t* data, uint8_t length);
/** /// <summary>Sets the FDMA preamble count.</summary>
* @brief Sets the FDMA preamble count.
* @param preambleCnt FDMA preamble count.
*/
void setPreambleCount(uint8_t preambleCnt); void setPreambleCount(uint8_t preambleCnt);
/** /// <summary>Helper to get how much space the ring buffer has for samples.</summary>
* @brief Helper to resize the FIFO buffer.
* @param size
*/
void resizeBuffer(uint16_t size);
/**
* @brief Helper to get how much space the ring buffer has for samples.
* @returns uint8_t Amount of space in ring buffer for samples.
*/
uint16_t getSpace() const; uint16_t getSpace() const;
private: private:
@ -87,12 +79,11 @@ namespace dmr
uint32_t m_preambleCnt; uint32_t m_preambleCnt;
/** /// <summary></summary>
* @brief Helper to write a raw byte to the DAC.
* @param c Byte.
*/
void writeByte(uint8_t c); void writeByte(uint8_t c);
}; };
} // namespace dmr } // namespace dmr
#endif // defined(ENABLE_DMR)
#endif // __DMR_DMO_TX_H__ #endif // __DMR_DMO_TX_H__

@ -1,20 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2009-2016 Jonathan Naylor, G4KLX
*
*/
/** /**
* @defgroup dmr_hfw Digital Mobile Radio * Digital Voice Modem - DSP Firmware (Hotspot)
* @brief Implementation for the ETSI TS-102 Digital Mobile Radio (DMR) standard. * GPLv2 Open Source. Use is subject to license terms.
* @ingroup hotspot_fw * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* @file DMRDefines.h * @package DVM / DSP Firmware (Hotspot)
* @ingroup dmr_hfw *
*/ */
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2016 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(__DMR_DEFINES_H__) #if !defined(__DMR_DEFINES_H__)
#define __DMR_DEFINES_H__ #define __DMR_DEFINES_H__
@ -26,11 +38,6 @@ namespace dmr
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @addtogroup dmr_hfw
* @{
*/
const uint32_t DMR_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate const uint32_t DMR_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
const uint32_t DMR_FRAME_LENGTH_BYTES = 33U; const uint32_t DMR_FRAME_LENGTH_BYTES = 33U;
@ -119,8 +126,7 @@ namespace dmr
const int8_t DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[] = { +3, -3, -3, -3, +3, -3, -3, +3, +3, +3, -3, +3, -3, +3, +3, +3, +3, -3, -3, +3, -3, -3, -3, +3 }; const int8_t DMR_MS_VOICE_SYNC_SYMBOLS_VALUES[] = { +3, -3, -3, -3, +3, -3, -3, +3, +3, +3, -3, +3, -3, +3, +3, +3, +3, -3, -3, +3, -3, -3, -3, +3 };
// 505 = DMR_FRAME_LENGTH_BYTES * 15 + 10 (BUFFER_LEN = DMR_FRAME_LENGTH_BYTES * NO_OF_FRAMES + 10) const uint32_t DMR_TX_BUFFER_LEN = 538U; // 538 = DMR_FRAME_LENGTH_BYTES * 16 + 10 (BUFFER_LEN = DMR_FRAME_LENGTH_BYTES * NO_OF_FRAMES + 10)
const uint32_t DMR_TX_BUFFER_LEN = 505U; // 15 frames + pad
// Data Type(s) // Data Type(s)
const uint8_t DT_VOICE_PI_HEADER = 0U; const uint8_t DT_VOICE_PI_HEADER = 0U;
@ -132,8 +138,6 @@ namespace dmr
const uint8_t DT_RATE_34_DATA = 8U; const uint8_t DT_RATE_34_DATA = 8U;
const uint8_t DT_IDLE = 9U; const uint8_t DT_IDLE = 9U;
const uint8_t DT_RATE_1_DATA = 10U; const uint8_t DT_RATE_1_DATA = 10U;
/** @} */
} // namespace dmr } // namespace dmr
#endif // __DMR_DEFINES_H__ #endif // __DMR_DEFINES_H__

@ -1,13 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. *
* * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2009-2017 Jonathan Naylor, G4KLX * it under the terms of the GNU General Public License as published by
* Copyright (C) 2017,2018 Andy Uribe, CA6JAU * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRIdleRX.h" #include "dmr/DMRIdleRX.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
@ -15,7 +35,7 @@
using namespace dmr; using namespace dmr;
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
@ -32,8 +52,9 @@ const uint8_t CONTROL_DATA = 0x40U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRIdleRX class. */ /// <summary>
/// Initializes a new instance of the DMRIdleRX class.
/// </summary>
DMRIdleRX::DMRIdleRX() : DMRIdleRX::DMRIdleRX() :
m_bitBuffer(0U), m_bitBuffer(0U),
m_buffer(), m_buffer(),
@ -44,16 +65,19 @@ DMRIdleRX::DMRIdleRX() :
/* stub */ /* stub */
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRIdleRX::reset() void DMRIdleRX::reset()
{ {
m_dataPtr = 0U; m_dataPtr = 0U;
m_endPtr = NOENDPTR; m_endPtr = NOENDPTR;
} }
/* Sample DMR bits from the air interface. */ /// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRIdleRX::databit(bool bit) void DMRIdleRX::databit(bool bit)
{ {
_WRITE_BIT(m_buffer, m_dataPtr, bit); _WRITE_BIT(m_buffer, m_dataPtr, bit);
@ -67,7 +91,7 @@ void DMRIdleRX::databit(bool bit)
if (m_endPtr >= DMR_IDLE_LENGTH_BITS) if (m_endPtr >= DMR_IDLE_LENGTH_BITS)
m_endPtr -= DMR_IDLE_LENGTH_BITS; m_endPtr -= DMR_IDLE_LENGTH_BITS;
DEBUG3("DMRIdleRx::databit() dataPtr/endPtr", m_dataPtr, m_endPtr); DEBUG3("DMRIdleRx: databit(): dataPtr/endPtr", m_dataPtr, m_endPtr);
} }
if (m_dataPtr == m_endPtr) { if (m_dataPtr == m_endPtr) {
@ -96,8 +120,10 @@ void DMRIdleRX::databit(bool bit)
m_dataPtr = 0U; m_dataPtr = 0U;
} }
/* Sets the DMR color code. */ /// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRIdleRX::setColorCode(uint8_t colorCode) void DMRIdleRX::setColorCode(uint8_t colorCode)
{ {
m_colorCode = colorCode; m_colorCode = colorCode;
@ -107,8 +133,12 @@ void DMRIdleRX::setColorCode(uint8_t colorCode)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* */ /// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <param name="buffer"></param>
void DMRIdleRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer) void DMRIdleRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{ {
for (uint8_t i = 0U; i < count; i++) { for (uint8_t i = 0U; i < count; i++) {
@ -155,4 +185,4 @@ void DMRIdleRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
} }
} }
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)

@ -1,26 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2018 Andy Uribe, CA6JAU
*
*/
/** /**
* @file DMRIdleRX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_mfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRIdleRX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_mfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU
*
* 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(__DMR_IDLE_RX_H__) #if !defined(__DMR_IDLE_RX_H__)
#define __DMR_IDLE_RX_H__ #define __DMR_IDLE_RX_H__
#include "Defines.h" #include "Defines.h"
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
namespace dmr namespace dmr
{ {
@ -32,34 +46,21 @@ namespace dmr
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for idle DMR mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for idle DMR mode operation.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRIdleRX { class DSP_FW_API DMRIdleRX {
public: public:
/** /// <summary>Initializes a new instance of the DMRIdleRX class.</summary>
* @brief Initializes a new instance of the DMRIdleRX class.
*/
DMRIdleRX(); DMRIdleRX();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample DMR bits from the air interface.</summary>
* @brief Sample DMR bits from the air interface.
* @param bit
*/
void databit(bool bit); void databit(bool bit);
/** /// <summary>Sets the DMR color code.</summary>
* @brief Sets the DMR color code.
* @param colorCode
*/
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
private: private:
@ -71,15 +72,11 @@ namespace dmr
uint8_t m_colorCode; uint8_t m_colorCode;
/** /// <summary></summary>
* @brief
* @param start
* @param count
* @param buffer
*/
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer); void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
}; };
} // namespace dmr } // namespace dmr
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)
#endif // __DMR_IDLE_RX_H__ #endif // __DMR_IDLE_RX_H__

@ -1,27 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2017 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRRX.h" #include "dmr/DMRRX.h"
using namespace dmr; using namespace dmr;
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRRX class. */ /// <summary>
/// Initializes a new instance of the DMRRX class.
/// </summary>
DMRRX::DMRRX() : DMRRX::DMRRX() :
m_slot1RX(false), m_slot1RX(false),
m_slot2RX(true) m_slot2RX(true)
@ -30,16 +51,19 @@ DMRRX::DMRRX() :
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRRX::reset() void DMRRX::reset()
{ {
m_slot1RX.reset(); m_slot1RX.reset();
m_slot2RX.reset(); m_slot2RX.reset();
} }
/* Sample DMR bits from the air interface. */ /// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void DMRRX::databit(bool bit, const uint8_t control) void DMRRX::databit(bool bit, const uint8_t control)
{ {
bool dcd1 = false; bool dcd1 = false;
@ -62,20 +86,24 @@ void DMRRX::databit(bool bit, const uint8_t control)
io.setDecode(dcd1 || dcd2); io.setDecode(dcd1 || dcd2);
} }
/* Sets the DMR color code. */ /// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRRX::setColorCode(uint8_t colorCode) void DMRRX::setColorCode(uint8_t colorCode)
{ {
m_slot1RX.setColorCode(colorCode); m_slot1RX.setColorCode(colorCode);
m_slot2RX.setColorCode(colorCode); m_slot2RX.setColorCode(colorCode);
} }
/* Sets the number of samples to delay before processing. */ /// <summary>
/// Sets the number of samples to delay before processing.
/// </summary>
/// <param name="delay">Number of samples to delay.</param>
void DMRRX::setRxDelay(uint8_t delay) void DMRRX::setRxDelay(uint8_t delay)
{ {
m_slot1RX.setRxDelay(delay); m_slot1RX.setRxDelay(delay);
m_slot2RX.setRxDelay(delay); m_slot2RX.setRxDelay(delay);
} }
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)

@ -1,66 +1,63 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2017 Andy Uribe, CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file DMRRX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRRX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DMR_RX_H__) #if !defined(__DMR_RX_H__)
#define __DMR_RX_H__ #define __DMR_RX_H__
#include "Defines.h" #include "Defines.h"
#include "dmr/DMRSlotRX.h" #include "dmr/DMRSlotRX.h"
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for duplex DMR mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for duplex DMR mode operation.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRRX { class DSP_FW_API DMRRX {
public: public:
/** /// <summary>Initializes a new instance of the DMRRX class.</summary>
* @brief Initializes a new instance of the DMRRX class.
*/
DMRRX(); DMRRX();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample DMR bits from the air interface.</summary>
* @brief Sample DMR bits from the air interface.
* @param bit
* @param[in] control
*/
void databit(bool bit, const uint8_t control); void databit(bool bit, const uint8_t control);
/** /// <summary>Sets the DMR color code.</summary>
* @brief Sets the DMR color code.
* @param colorCode
*/
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
/** /// <summary>Sets the number of samples to delay before processing.</summary>
* @brief Sets the number of samples to delay before processing.
* @param delay
*/
void setRxDelay(uint8_t delay); void setRxDelay(uint8_t delay);
private: private:
@ -69,5 +66,6 @@ namespace dmr
}; };
} // namespace dmr } // namespace dmr
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)
#endif // __DMR_RX_H__ #endif // __DMR_RX_H__

@ -1,14 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2009-2017 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017,2018 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRSlotRX.h" #include "dmr/DMRSlotRX.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
@ -16,7 +36,7 @@
using namespace dmr; using namespace dmr;
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
@ -36,10 +56,11 @@ const uint8_t CONTROL_DATA = 0x40U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRSlotRX class. */ /// <summary>
/// Initializes a new instance of the DMRSlotRX class.
/// </summary>
DMRSlotRX::DMRSlotRX(bool slot) : DMRSlotRX::DMRSlotRX(bool slot) :
m_slot(slot), m_slot(false),
m_bitBuffer(0x00U), m_bitBuffer(0x00U),
m_buffer(), m_buffer(),
m_dataPtr(0U), m_dataPtr(0U),
@ -58,8 +79,9 @@ DMRSlotRX::DMRSlotRX(bool slot) :
/* stub */ /* stub */
} }
/* Helper to set data values for start of Rx. */ /// <summary>
/// Helper to set data values for start of Rx.
/// </summary>
void DMRSlotRX::start() void DMRSlotRX::start()
{ {
m_dataPtr = 0U; m_dataPtr = 0U;
@ -67,20 +89,28 @@ void DMRSlotRX::start()
m_control = CONTROL_NONE; m_control = CONTROL_NONE;
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void DMRSlotRX::reset() void DMRSlotRX::reset()
{ {
m_syncPtr = 0U;
m_dataPtr = 0U; m_dataPtr = 0U;
m_delayPtr = 0U; m_delayPtr = 0U;
m_bitBuffer = 0U; m_bitBuffer = 0U;
resetSlot(); m_control = CONTROL_NONE;
m_syncCount = 0U;
m_state = DMRRXS_NONE;
m_startPtr = 0U;
m_endPtr = NOENDPTR;
} }
/* Sample DMR bits from the air interface. */ /// <summary>
/// Sample DMR bits from the air interface.
/// </summary>
/// <param name="bit"></param>
bool DMRSlotRX::databit(bool bit) bool DMRSlotRX::databit(bool bit)
{ {
uint16_t min, max; uint16_t min, max;
@ -141,7 +171,7 @@ bool DMRSlotRX::databit(bool bit)
switch (dataType) { switch (dataType) {
case DT_DATA_HEADER: case DT_DATA_HEADER:
DEBUG3("DMRSlotRX::databit() data header found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): data header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMRRXS_DATA; m_state = DMRRXS_DATA;
m_type = 0x00U; m_type = 0x00U;
@ -150,33 +180,33 @@ bool DMRSlotRX::databit(bool bit)
case DT_RATE_34_DATA: case DT_RATE_34_DATA:
case DT_RATE_1_DATA: case DT_RATE_1_DATA:
if (m_state == DMRRXS_DATA) { if (m_state == DMRRXS_DATA) {
DEBUG3("DMRSlotRX::databit() data payload found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): data payload found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_type = dataType; m_type = dataType;
} }
break; break;
case DT_VOICE_LC_HEADER: case DT_VOICE_LC_HEADER:
DEBUG3("DMRSlotRX::databit() voice header found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): voice header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMRRXS_VOICE; m_state = DMRRXS_VOICE;
break; break;
case DT_VOICE_PI_HEADER: case DT_VOICE_PI_HEADER:
if (m_state == DMRRXS_VOICE) { if (m_state == DMRRXS_VOICE) {
DEBUG3("DMRSlotRX::databit() voice pi header found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): voice pi header found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
} }
m_state = DMRRXS_VOICE; m_state = DMRRXS_VOICE;
break; break;
case DT_TERMINATOR_WITH_LC: case DT_TERMINATOR_WITH_LC:
if (m_state == DMRRXS_VOICE) { if (m_state == DMRRXS_VOICE) {
DEBUG3("DMRSlotRX::databit() voice terminator found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): voice terminator found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMRRXS_NONE; m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR; m_endPtr = NOENDPTR;
} }
break; break;
default: // DT_CSBK default: // DT_CSBK
DEBUG3("DMRSlotRX::databit() csbk found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): csbk found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMRRXS_NONE; m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR; m_endPtr = NOENDPTR;
@ -186,7 +216,7 @@ bool DMRSlotRX::databit(bool bit)
} }
else if (m_control == CONTROL_VOICE) { else if (m_control == CONTROL_VOICE) {
// Voice sync // Voice sync
DEBUG3("DMRSlotRX::databit() voice sync found slot/pos", m_slot ? 2U : 1U, m_syncPtr); DEBUG3("DMRSlotRX: databit(): voice sync found slot/pos", m_slot ? 2U : 1U, m_syncPtr);
writeRSSIData(frame); writeRSSIData(frame);
m_state = DMRRXS_VOICE; m_state = DMRRXS_VOICE;
m_syncCount = 0U; m_syncCount = 0U;
@ -196,9 +226,10 @@ bool DMRSlotRX::databit(bool bit)
if (m_state != DMRRXS_NONE) { if (m_state != DMRRXS_NONE) {
m_syncCount++; m_syncCount++;
if (m_syncCount >= MAX_SYNC_LOST_FRAMES) { if (m_syncCount >= MAX_SYNC_LOST_FRAMES) {
DEBUG1("DMRSlotRX::databit() sync timeout, lost lock"); DEBUG1("DMRSlotRX: databit(): sync timeout, lost lock");
serial.writeDMRLost(m_slot); serial.writeDMRLost(m_slot);
resetSlot(); m_state = DMRRXS_NONE;
m_endPtr = NOENDPTR;
} }
} }
@ -220,9 +251,6 @@ bool DMRSlotRX::databit(bool bit)
} }
} }
} }
// end of this slot, reset some items for the next slot
m_control = CONTROL_NONE;
} }
m_dataPtr++; m_dataPtr++;
@ -232,15 +260,19 @@ bool DMRSlotRX::databit(bool bit)
return m_state != DMRRXS_NONE; return m_state != DMRRXS_NONE;
} }
/* Sets the DMR color code. */ /// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRSlotRX::setColorCode(uint8_t colorCode) void DMRSlotRX::setColorCode(uint8_t colorCode)
{ {
m_colorCode = colorCode; m_colorCode = colorCode;
} }
/* Sets the number of samples to delay before processing. */ /// <summary>
/// Sets the number of samples to delay before processing.
/// </summary>
/// <param name="delay">Number of samples to delay.</param>
void DMRSlotRX::setRxDelay(uint8_t delay) void DMRSlotRX::setRxDelay(uint8_t delay)
{ {
m_delay = delay; m_delay = delay;
@ -250,8 +282,10 @@ void DMRSlotRX::setRxDelay(uint8_t delay)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Frame synchronization correlator. */ /// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <param name="first"></param>
void DMRSlotRX::correlateSync() void DMRSlotRX::correlateSync()
{ {
// unpack sync bytes // unpack sync bytes
@ -269,11 +303,11 @@ void DMRSlotRX::correlateSync()
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]); errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_DATA_SYNC_BYTES[i]);
DEBUG2("DMRSlotRX::correlateSync() sync errs", errs); DEBUG2("DMRSlotRX: correlateSync(): correlateSync errs", errs);
DEBUG4("DMRSlotRX::correlateSync() sync [b0 - b2]", sync[0], sync[1], sync[2]); DEBUG4("DMRSlotRX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
DEBUG4("DMRSlotRX::correlateSync() sync [b3 - b5]", sync[3], sync[4], sync[5]); DEBUG4("DMRSlotRX: correlateSync(): sync [b3 - b5]", sync[3], sync[4], sync[5]);
DEBUG2("DMRSlotRX::correlateSync() sync [b6]", sync[6]); DEBUG2("DMRSlotRX: correlateSync(): sync [b6]", sync[6]);
m_control = CONTROL_DATA; m_control = CONTROL_DATA;
m_syncPtr = m_dataPtr; m_syncPtr = m_dataPtr;
@ -286,17 +320,17 @@ void DMRSlotRX::correlateSync()
if (m_endPtr >= DMR_BUFFER_LENGTH_BITS) if (m_endPtr >= DMR_BUFFER_LENGTH_BITS)
m_endPtr -= DMR_BUFFER_LENGTH_BITS; m_endPtr -= DMR_BUFFER_LENGTH_BITS;
DEBUG4("DMRSlotRX::correlateSync() dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr); DEBUG4("DMRSlotRX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
} else if (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) { } else if (countBits64((m_bitBuffer & DMR_SYNC_BITS_MASK) ^ DMR_MS_VOICE_SYNC_BITS) <= MAX_SYNC_BYTES_ERRS) {
uint8_t errs = 0U; uint8_t errs = 0U;
for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < DMR_SYNC_BYTES_LENGTH; i++)
errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]); errs += countBits8((sync[i] & DMR_SYNC_BYTES_MASK[i]) ^ DMR_MS_VOICE_SYNC_BYTES[i]);
DEBUG2("DMRSlotRX::correlateSync() sync errs", errs); DEBUG2("DMRSlotRX: correlateSync(): correlateSync errs", errs);
DEBUG4("DMRSlotRX::correlateSync() sync [b0 - b2]", sync[0], sync[1], sync[2]); DEBUG4("DMRSlotRX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
DEBUG4("DMRSlotRX::correlateSync() sync [b3 - b5]", sync[3], sync[4], sync[5]); DEBUG4("DMRSlotRX: correlateSync(): sync [b3 - b5]", sync[3], sync[4], sync[5]);
DEBUG2("DMRSlotRX::correlateSync() sync [b6]", sync[6]); DEBUG2("DMRSlotRX: correlateSync(): sync [b6]", sync[6]);
m_control = CONTROL_VOICE; m_control = CONTROL_VOICE;
m_syncPtr = m_dataPtr; m_syncPtr = m_dataPtr;
@ -309,27 +343,16 @@ void DMRSlotRX::correlateSync()
if (m_endPtr >= DMR_BUFFER_LENGTH_BITS) if (m_endPtr >= DMR_BUFFER_LENGTH_BITS)
m_endPtr -= DMR_BUFFER_LENGTH_BITS; m_endPtr -= DMR_BUFFER_LENGTH_BITS;
DEBUG4("DMRSlotRX::correlateSync() dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr); DEBUG4("DMRSlotRX: correlateSync(): dataPtr/startPtr/endPtr", m_dataPtr, m_startPtr, m_endPtr);
} }
} }
/* */ /// <summary>
///
void DMRSlotRX::resetSlot() /// </summary>
{ /// <param name="start"></param>
m_syncPtr = 0U; /// <param name="count"></param>
/// <param name="buffer"></param>
m_control = CONTROL_NONE;
m_syncCount = 0U;
m_state = DMRRXS_NONE;
m_startPtr = 0U;
m_endPtr = NOENDPTR;
m_type = 0U;
m_n = 0U;
}
/* */
void DMRSlotRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer) void DMRSlotRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
{ {
for (uint8_t i = 0U; i < count; i++) { for (uint8_t i = 0U; i < count; i++) {
@ -376,8 +399,10 @@ void DMRSlotRX::bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer)
} }
} }
/* */ /// <summary>
///
/// </summary>
/// <param name="frame"></param>
void DMRSlotRX::writeRSSIData(uint8_t* frame) void DMRSlotRX::writeRSSIData(uint8_t* frame)
{ {
#if defined(SEND_RSSI_DATA) #if defined(SEND_RSSI_DATA)
@ -392,4 +417,4 @@ void DMRSlotRX::writeRSSIData(uint8_t* frame)
#endif #endif
} }
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)

@ -1,27 +1,41 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2017,2018 Andy Uribe, CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file DMRSlotRX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRSlotRX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__DMR_SLOT_RX_H__) #if !defined(__DMR_SLOT_RX_H__)
#define __DMR_SLOT_RX_H__ #define __DMR_SLOT_RX_H__
#include "Defines.h" #include "Defines.h"
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#if defined(DUPLEX) #if defined(ENABLE_DMR) && defined(DUPLEX)
namespace dmr namespace dmr
{ {
@ -31,56 +45,33 @@ namespace dmr
const uint16_t DMR_BUFFER_LENGTH_BITS = 576U; const uint16_t DMR_BUFFER_LENGTH_BITS = 576U;
/**
* @brief DMR Slot Receiver State
* @ingroup dmr_hfw
*/
enum DMRRX_STATE { enum DMRRX_STATE {
DMRRXS_NONE, //! None DMRRXS_NONE,
DMRRXS_VOICE, //! Voice Data DMRRXS_VOICE,
DMRRXS_DATA //! PDU Data DMRRXS_DATA
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for DMR slots.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for DMR slots.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRSlotRX { class DSP_FW_API DMRSlotRX {
public: public:
/** /// <summary>Initializes a new instance of the DMRSlotRX class.</summary>
* @brief Initializes a new instance of the DMRSlotRX class.
* @param slot
*/
DMRSlotRX(bool slot); DMRSlotRX(bool slot);
/** /// <summary>Helper to set data values for start of Rx.</summary>
* @brief Helper to set data values for start of Rx.
*/
void start(); void start();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample DMR bits from the air interface.</summary>
* @brief Sample DMR bits from the air interface.
* @param bit
*/
bool databit(bool bit); bool databit(bool bit);
/** /// <summary>Sets the DMR color code.</summary>
* @brief Sets the DMR color code.
* @param colorCode
*/
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
/** /// <summary>Sets the number of samples to delay before processing.</summary>
* @brief Sets the number of samples to delay before processing.
* @param delay
*/
void setRxDelay(uint8_t delay); void setRxDelay(uint8_t delay);
@ -109,29 +100,16 @@ namespace dmr
uint8_t m_type; uint8_t m_type;
/** /// <summary>Frame synchronization correlator.</summary>
* @brief Frame synchronization correlator.
*/
void correlateSync(); void correlateSync();
/**
* @brief /// <summary></summary>
*/
void resetSlot();
/**
* @brief
* @param start
* @param count
* @param buffer
*/
void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer); void bitsToBytes(uint16_t start, uint8_t count, uint8_t* buffer);
/** /// <summary></summary>
* @brief
* @param frame
*/
void writeRSSIData(uint8_t* frame); void writeRSSIData(uint8_t* frame);
}; };
} // namespace dmr } // namespace dmr
#endif // DUPLEX #endif // defined(ENABLE_DMR) && defined(DUPLEX)
#endif // __DMR_SLOT_RX_H__ #endif // __DMR_SLOT_RX_H__

@ -1,17 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. *
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * 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
* Copyright (C) 2015 Jonathan Naylor, G4KLX * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
using namespace dmr; using namespace dmr;
#if defined(ENABLE_DMR)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -209,15 +231,20 @@ const uint32_t DECODING_TABLE_1987[] = {
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRSlotType class. */ /// <summary>
/// Initializes a new instance of the DMRSlotType class.
/// </summary>
DMRSlotType::DMRSlotType() DMRSlotType::DMRSlotType()
{ {
/* stub */ /* stub */
} }
/* Decodes DMR slot type. */ /// <summary>
/// Decodes DMR slot type.
/// </summary>
/// <param name="frame"></param>
/// <param name="colorCode"></param>
/// <param name="dataType"></param>
void DMRSlotType::decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const void DMRSlotType::decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const
{ {
uint8_t slotType[3U]; uint8_t slotType[3U];
@ -236,8 +263,12 @@ void DMRSlotType::decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& data
dataType = (code >> 0) & 0x0FU; dataType = (code >> 0) & 0x0FU;
} }
/* Encodes DMR slot type. */ /// <summary>
/// Encodes DMR slot type.
/// </summary>
/// <param name="colorCode"></param>
/// <param name="dataType"></param>
/// <param name="frame"></param>
void DMRSlotType::encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const void DMRSlotType::encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const
{ {
uint8_t slotType[3U]; uint8_t slotType[3U];
@ -259,8 +290,11 @@ void DMRSlotType::encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) co
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* */ /// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
uint8_t DMRSlotType::decode2087(const uint8_t* data) const uint8_t DMRSlotType::decode2087(const uint8_t* data) const
{ {
uint32_t code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5); uint32_t code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5);
@ -273,8 +307,20 @@ uint8_t DMRSlotType::decode2087(const uint8_t* data) const
return code >> 11; return code >> 11;
} }
/* */ /// <summary>
///
/// </summary>
/// <remarks>
/// Compute the syndrome corresponding to the given pattern, i.e., the
/// remainder after dividing the pattern (when considering it as the vector
/// representation of a polynomial) by the generator polynomial, GENPOL.
/// In the program this pattern has several meanings: (1) pattern = infomation
/// bits, when constructing the encoding table; (2) pattern = error pattern,
/// when constructing the decoding table; and (3) pattern = received vector, to
/// obtain its syndrome in decoding.
/// </remarks>
/// <param name="pattern"></param>
/// <returns></returns>
uint32_t DMRSlotType::getSyndrome1987(uint32_t pattern) const uint32_t DMRSlotType::getSyndrome1987(uint32_t pattern) const
{ {
unsigned int aux = X18; unsigned int aux = X18;
@ -290,3 +336,5 @@ uint32_t DMRSlotType::getSyndrome1987(uint32_t pattern) const
return pattern; return pattern;
} }
#endif // defined(ENABLE_DMR)

@ -1,69 +1,64 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015 Jonathan Naylor, G4KLX
*
*/
/** /**
* @file DMRSlotType.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRSlotType.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015 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(__DMR_SLOT_TYPE_H__) #if !defined(__DMR_SLOT_TYPE_H__)
#define __DMR_SLOT_TYPE_H__ #define __DMR_SLOT_TYPE_H__
#include "Defines.h" #include "Defines.h"
#if defined(ENABLE_DMR)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
//
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Represents DMR slot type.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRSlotType { class DSP_FW_API DMRSlotType {
public: public:
/** /// <summary>Initializes a new instance of the DMRSlotType class.</summary>
* @brief Initializes a new instance of the DMRSlotType class.
*/
DMRSlotType(); DMRSlotType();
/** /// <summary>Decodes DMR slot type.</summary>
* @brief Decodes DMR slot type.
* @param[in] frame
* @param[out] colorCode
* @param[out] dataType
*/
void decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const; void decode(const uint8_t* frame, uint8_t& colorCode, uint8_t& dataType) const;
/** /// <summary>Encodes DMR slot type.</summary>
* @brief Encodes DMR slot type.
* @param colorCode
* @param dataType
* @param[out] frame
*/
void encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const; void encode(uint8_t colorCode, uint8_t dataType, uint8_t* frame) const;
private: private:
/** /// <summary></summary>
* @brief
* @param[in] data
* @returns uint8_t
*/
uint8_t decode2087(const uint8_t* data) const; uint8_t decode2087(const uint8_t* data) const;
/** /// <summary></summary>
* @brief
* @param pattern
* @returns uint32_t
*/
uint32_t getSyndrome1987(uint32_t pattern) const; uint32_t getSyndrome1987(uint32_t pattern) const;
}; };
} // namespace dmr } // namespace dmr
#endif // defined(ENABLE_DMR)
#endif // __DMR_SLOT_TYPE_H__ #endif // __DMR_SLOT_TYPE_H__

@ -1,20 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2017 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016 by Colin Durbridge G4EML
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2017 by Andy Uribe CA6JAU
* * Copyright (C) 2021-2022 by Bryan Biedenkapp N2PLL
* Copyright (C) 2009-2017 Jonathan Naylor, G4KLX *
* Copyright (C) 2016 Colin Durbridge, G4EML * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021-2022 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "dmr/DMRSlotType.h" #include "dmr/DMRSlotType.h"
using namespace dmr; using namespace dmr;
#if defined(ENABLE_DMR)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -44,8 +66,9 @@ const uint32_t ABORT_COUNT = 6U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the DMRTX class. */ /// <summary>
/// Initializes a new instance of the DMRTX class.
/// </summary>
DMRTX::DMRTX() : DMRTX::DMRTX() :
m_fifo(), m_fifo(),
m_state(DMRTXSTATE_IDLE), m_state(DMRTXSTATE_IDLE),
@ -76,8 +99,9 @@ DMRTX::DMRTX() :
m_abortCount[1U] = 0U; m_abortCount[1U] = 0U;
} }
/* Process local buffer and transmit on the air interface. */ /// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void DMRTX::process() void DMRTX::process()
{ {
if (m_state == DMRTXSTATE_IDLE) if (m_state == DMRTXSTATE_IDLE)
@ -109,6 +133,8 @@ void DMRTX::process()
m_state = DMRTXSTATE_SLOT1; m_state = DMRTXSTATE_SLOT1;
break; break;
} }
DEBUG2("DMRTX: process(): poLen", m_poLen);
} }
if (m_poLen > 0U) { if (m_poLen > 0U) {
@ -132,15 +158,19 @@ void DMRTX::process()
} }
} }
/* Write slot 1 data to the local buffer. */ /// <summary>
/// Write slot 1 data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeData1(const uint8_t* data, uint8_t length) uint8_t DMRTX::writeData1(const uint8_t* data, uint8_t length)
{ {
if (length != (DMR_FRAME_LENGTH_BYTES + 1U)) if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo[0U].getSpace(); uint16_t space = m_fifo[0U].getSpace();
DEBUG3("DMRTX::writeData1() dataLength/fifoLength", length, space); DEBUG3("DMRTX: writeData1(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES) if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL; return RSN_RINGBUFF_FULL;
@ -159,15 +189,19 @@ uint8_t DMRTX::writeData1(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
/* Write slot 2 data to the local buffer. */ /// <summary>
/// Write slot 2 data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeData2(const uint8_t* data, uint8_t length) uint8_t DMRTX::writeData2(const uint8_t* data, uint8_t length)
{ {
if (length != (DMR_FRAME_LENGTH_BYTES + 1U)) if (length != (DMR_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo[1U].getSpace(); uint16_t space = m_fifo[1U].getSpace();
DEBUG3("DMRTX::writeData2() dataLength/fifoLength", length, space); DEBUG3("DMRTX: writeData2(): dataLength/fifoLength", length, space);
if (space < DMR_FRAME_LENGTH_BYTES) if (space < DMR_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL; return RSN_RINGBUFF_FULL;
@ -186,8 +220,12 @@ uint8_t DMRTX::writeData2(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
/* Write short LC data to the local buffer. */ /// <summary>
/// Write short LC data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeShortLC(const uint8_t* data, uint8_t length) uint8_t DMRTX::writeShortLC(const uint8_t* data, uint8_t length)
{ {
if (length != 9U) if (length != 9U)
@ -204,8 +242,12 @@ uint8_t DMRTX::writeShortLC(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
/* Write abort data to the local buffer. */ /// <summary>
/// Write abort data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t DMRTX::writeAbort(const uint8_t* data, uint8_t length) uint8_t DMRTX::writeAbort(const uint8_t* data, uint8_t length)
{ {
if (length != 1U) if (length != 1U)
@ -227,8 +269,10 @@ uint8_t DMRTX::writeAbort(const uint8_t* data, uint8_t length)
} }
} }
/* Helper to set the start state for Tx. */ /// <summary>
/// Helper to set the start state for Tx.
/// </summary>
/// <param name="start"></param>
void DMRTX::setStart(bool start) void DMRTX::setStart(bool start)
{ {
m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE; m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE;
@ -241,29 +285,37 @@ void DMRTX::setStart(bool start)
m_abort[1U] = false; m_abort[1U] = false;
} }
/* Helper to set the calibration state for Tx. */ /// <summary>
/// Helper to set the calibration state for Tx.
/// </summary>
/// <param name="start"></param>
void DMRTX::setCal(bool start) void DMRTX::setCal(bool start)
{ {
m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE; m_state = start ? DMRTXSTATE_CAL : DMRTXSTATE_IDLE;
} }
/* Helper to get how much space the slot 1 ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the slot 1 ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t DMRTX::getSpace1() const uint8_t DMRTX::getSpace1() const
{ {
return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U); return m_fifo[0U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
} }
/* Helper to get how much space the slot 2 ring buffer has for samples. */ /// <summary>
/// Helper to get how much space the slot 2 ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t DMRTX::getSpace2() const uint8_t DMRTX::getSpace2() const
{ {
return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U); return m_fifo[1U].getSpace() / (DMR_FRAME_LENGTH_BYTES + 2U);
} }
/* Sets the ignore flags for setting the CACH Access Type bit. */ /// <summary>
/// Sets the ignore flags for setting the CACH Access Type bit.
/// </summary>
/// <param name="slot"></param>
void DMRTX::setIgnoreCACH_AT(uint8_t slot) void DMRTX::setIgnoreCACH_AT(uint8_t slot)
{ {
m_cachATControl = slot; m_cachATControl = slot;
@ -272,8 +324,10 @@ void DMRTX::setIgnoreCACH_AT(uint8_t slot)
} }
} }
/* Sets the DMR color code. */ /// <summary>
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode">Color code.</param>
void DMRTX::setColorCode(uint8_t colorCode) void DMRTX::setColorCode(uint8_t colorCode)
{ {
::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES); ::memcpy(m_idle, IDLE_DATA, DMR_FRAME_LENGTH_BYTES);
@ -282,32 +336,26 @@ void DMRTX::setColorCode(uint8_t colorCode)
slotType.encode(colorCode, DT_IDLE, m_idle); slotType.encode(colorCode, DT_IDLE, m_idle);
} }
/* Helper to reset data values to defaults for slot 1 FIFO. */ /// <summary>
/// Helper to reset data values to defaults for slot 1 FIFO.
/// </summary>
void DMRTX::resetFifo1() void DMRTX::resetFifo1()
{ {
m_fifo[0U].reset(); m_fifo[0U].reset();
} }
/* Helper to reset data values to defaults for slot 2 FIFO. */ /// <summary>
/// Helper to reset data values to defaults for slot 2 FIFO.
/// </summary>
void DMRTX::resetFifo2() void DMRTX::resetFifo2()
{ {
m_fifo[1U].reset(); m_fifo[1U].reset();
} }
/* Helper to resize the FIFO buffer. */ /// <summary>
///
void DMRTX::resizeBuffer(uint16_t size) /// </summary>
{ /// <returns></returns>
m_fifo[0U].reset();
m_fifo[1U].reset();
m_fifo[0U].reinitialize(size);
m_fifo[1U].reinitialize(size);
}
/* */
uint32_t DMRTX::getFrameCount() uint32_t DMRTX::getFrameCount()
{ {
return m_frameCount; return m_frameCount;
@ -317,8 +365,10 @@ uint32_t DMRTX::getFrameCount()
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to generate data. */ /// <summary>
///
/// </summary>
/// <param name="slotIndex"></param>
void DMRTX::createData(uint8_t slotIndex) void DMRTX::createData(uint8_t slotIndex)
{ {
if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) { if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) {
@ -346,8 +396,47 @@ void DMRTX::createData(uint8_t slotIndex)
m_poPtr = 0U; m_poPtr = 0U;
} }
/* Helper to generate the common access channel. */ /// <summary>
///
/// </summary>
void DMRTX::createCal()
{
// 1.2 kHz sine wave generation
if (m_modemState == STATE_DMR_CAL) {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = DMR_START_SYNC;
m_markBuffer[i] = MARK_NONE;
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
}
// 80 Hz square wave generation
if (m_modemState == STATE_DMR_LF_CAL) {
for (unsigned int i = 0U; i < 7U; i++) {
m_poBuffer[i] = 0x55U; // +3, +3, ... pattern
m_markBuffer[i] = MARK_NONE;
}
m_poBuffer[7U] = 0x5FU; // +3, +3, -3, -3 pattern
for (unsigned int i = 8U; i < 15U; i++) {
m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern
m_markBuffer[i] = MARK_NONE;
}
m_poLen = 15U;
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
/// <param name="txSlotIndex"></param>
/// <param name="rxSlotIndex"></param>
void DMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex) void DMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
{ {
m_frameCount++; m_frameCount++;
@ -407,43 +496,11 @@ void DMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex)
m_cachPtr += 3U; m_cachPtr += 3U;
} }
/* Helper to generate calibration data. */ /// <summary>
///
void DMRTX::createCal() /// </summary>
{ /// <param name="c"></param>
// 1.2 kHz sine wave generation /// <param name="control"></param>
if (m_modemState == STATE_DMR_CAL) {
for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = DMR_START_SYNC;
m_markBuffer[i] = MARK_NONE;
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
}
// 80 Hz square wave generation
if (m_modemState == STATE_DMR_LF_CAL) {
for (unsigned int i = 0U; i < 7U; i++) {
m_poBuffer[i] = 0x55U; // +3, +3, ... pattern
m_markBuffer[i] = MARK_NONE;
}
m_poBuffer[7U] = 0x5FU; // +3, +3, -3, -3 pattern
for (unsigned int i = 8U; i < 15U; i++) {
m_poBuffer[i] = 0xFFU; // -3, -3, ... pattern
m_markBuffer[i] = MARK_NONE;
}
m_poLen = 15U;
}
m_poLen = DMR_FRAME_LENGTH_BYTES;
m_poPtr = 0U;
}
/* Helper to write a raw byte to the DAC. */
void DMRTX::writeByte(uint8_t c, uint8_t control) void DMRTX::writeByte(uint8_t c, uint8_t control)
{ {
uint8_t bit; uint8_t bit;
@ -468,3 +525,5 @@ void DMRTX::writeByte(uint8_t c, uint8_t control)
io.write(&bit, 1, &controlToWrite); io.write(&bit, 1, &controlToWrite);
} }
} }
#endif // defined(ENABLE_DMR)

@ -1,21 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2016 Colin Durbridge, G4EML
* Copyright (C) 2017 Andy Uribe, CA6JAU
* Copyright (C) 2021-2022 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file DMRTX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup dmr_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file DMRTX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup dmr_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2021-2022 by Bryan Biedenkapp N2PLL
*
* 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(__DMR_TX_H__) #if !defined(__DMR_TX_H__)
#define __DMR_TX_H__ #define __DMR_TX_H__
@ -23,127 +37,66 @@
#include "dmr/DMRDefines.h" #include "dmr/DMRDefines.h"
#include "SerialBuffer.h" #include "SerialBuffer.h"
#if defined(ENABLE_DMR)
namespace dmr namespace dmr
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief DMR Duplex Transmitter State
* @ingroup dmr_hfw
*/
enum DMRTXSTATE { enum DMRTXSTATE {
DMRTXSTATE_IDLE, //! Idle DMRTXSTATE_IDLE,
DMRTXSTATE_SLOT1, //! Slot 1 DMRTXSTATE_SLOT1,
DMRTXSTATE_CACH1, //! Common Access Channel 1 DMRTXSTATE_CACH1,
DMRTXSTATE_SLOT2, //! Slot 2 DMRTXSTATE_SLOT2,
DMRTXSTATE_CACH2, //! Common Access Channel 2 DMRTXSTATE_CACH2,
DMRTXSTATE_CAL //! Calibration DMRTXSTATE_CAL
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for duplex DMR mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for duplex DMR mode operation.
* @ingroup dmr_hfw
*/
class DSP_FW_API DMRTX { class DSP_FW_API DMRTX {
public: public:
/** /// <summary>Initializes a new instance of the DMRTX class.</summary>
* @brief Initializes a new instance of the DMRTX class.
*/
DMRTX(); DMRTX();
/** /// <summary>Process local buffer and transmit on the air interface.</summary>
* @brief Process local buffer and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write slot 1 data to the local buffer.</summary>
* @brief Write slot 1 data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeData1(const uint8_t* data, uint8_t length); uint8_t writeData1(const uint8_t* data, uint8_t length);
/** /// <summary>Write slot 2 data to the local buffer.</summary>
* @brief Write slot 2 data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeData2(const uint8_t* data, uint8_t length); uint8_t writeData2(const uint8_t* data, uint8_t length);
/** /// <summary>Write short LC data to the local buffer.</summary>
* @brief Write short LC data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeShortLC(const uint8_t* data, uint8_t length); uint8_t writeShortLC(const uint8_t* data, uint8_t length);
/** /// <summary>Write abort data to the local buffer.</summary>
* @brief Write abort data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeAbort(const uint8_t* data, uint8_t length); uint8_t writeAbort(const uint8_t* data, uint8_t length);
/** /// <summary>Helper to set the start state for Tx.</summary>
* @brief Helper to set the start state for Tx.
* @param start
*/
void setStart(bool start); void setStart(bool start);
/** /// <summary>Helper to set the calibration state for Tx.</summary>
* @brief Helper to set the calibration state for Tx.
* @param start
*/
void setCal(bool start); void setCal(bool start);
/** /// <summary>Helper to get how much space the slot 1 ring buffer has for samples.</summary>
* @brief Helper to get how much space the slot 1 ring buffer has for samples.
* @returns uint8_t Amount of space in the slot 1 ring buffer.
*/
uint8_t getSpace1() const; uint8_t getSpace1() const;
/** /// <summary>Helper to get how much space the slot 2 ring buffer has for samples.</summary>
* @brief Helper to get how much space the slot 2 ring buffer has for samples.
* @returns uint8_t Amount of space in the slot 2 ring buffer.
*/
uint8_t getSpace2() const; uint8_t getSpace2() const;
/** /// <summary>Sets the ignore flags for setting the CACH Access Type bit.</summary>
* @brief Sets the ignore flags for setting the CACH Access Type bit.
* @param slot DMR slot number.
*/
void setIgnoreCACH_AT(uint8_t slot); void setIgnoreCACH_AT(uint8_t slot);
/** /// <summary>Sets the DMR color code.</summary>
* @brief Sets the DMR color code.
* @param colorCode Color code.
*/
void setColorCode(uint8_t colorCode); void setColorCode(uint8_t colorCode);
/** /// <summary>Helper to reset data values to defaults for slot 1 FIFO.</summary>
* @brief Helper to reset data values to defaults for slot 1 FIFO.
*/
void resetFifo1(); void resetFifo1();
/** /// <summary>Helper to reset data values to defaults for slot 2 FIFO.</summary>
* @brief Helper to reset data values to defaults for slot 2 FIFO.
*/
void resetFifo2(); void resetFifo2();
/// <summary></summary>
/**
* @brief Helper to resize the FIFO buffer.
* @param size
*/
void resizeBuffer(uint16_t size);
/**
* @brief
* @returns uint32_t
*/
uint32_t getFrameCount(); uint32_t getFrameCount();
private: private:
@ -172,29 +125,18 @@ namespace dmr
uint8_t m_controlPrev; uint8_t m_controlPrev;
/** /// <summary></summary>
* @brief Helper to generate data.
* @param slotIndex
*/
void createData(uint8_t slotIndex); void createData(uint8_t slotIndex);
/** /// <summary></summary>
* @brief Helper to generate the common access channel.
* @param txSlotIndex
* @param rxSlotIndex
*/
void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex); void createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex);
/** /// <summary></summary>
* @brief Helper to generate calibration data.
*/
void createCal(); void createCal();
/** /// <summary></summary>
* @brief Helper to write a raw byte to the DAC.
* @param c Byte.
* @param control
*/
void writeByte(uint8_t c, uint8_t control); void writeByte(uint8_t c, uint8_t control);
}; };
} // namespace dmr } // namespace dmr
#endif // defined(ENABLE_DMR)
#endif // __DMR_TX_H__ #endif // __DMR_TX_H__

@ -1,18 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2018 by Andy Uribe CA6JAU
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2020 by Jonathan Naylor G4KLX
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2018 Andy Uribe, CA6JAU * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2020 Jonathan Naylor, G4KLX * 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 "Globals.h" #include "Globals.h"
#include "nxdn/CalNXDN.h" #include "nxdn/CalNXDN.h"
using namespace nxdn; using namespace nxdn;
#if defined(ENABLE_NXDN)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -52,8 +74,9 @@ const uint8_t NXDN_CAL1K[4][49] = {
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the CalNXDN class. */ /// <summary>
/// Initializes a new instance of the CalNXDN class.
/// </summary>
CalNXDN::CalNXDN() : CalNXDN::CalNXDN() :
m_transmit(false), m_transmit(false),
m_state(NXDNCAL1K_IDLE), m_state(NXDNCAL1K_IDLE),
@ -62,8 +85,9 @@ CalNXDN::CalNXDN() :
/* stub */ /* stub */
} }
/* Process local state and transmit on the air interface. */ /// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalNXDN::process() void CalNXDN::process()
{ {
if (m_transmit) { if (m_transmit) {
@ -98,8 +122,12 @@ void CalNXDN::process()
} }
} }
/* Write NXDN calibration data to the local buffer. */ /// <summary>
/// Write P25 calibration data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalNXDN::write(const uint8_t* data, uint16_t length) uint8_t CalNXDN::write(const uint8_t* data, uint16_t length)
{ {
if (length != 1U) if (length != 1U)
@ -112,3 +140,5 @@ uint8_t CalNXDN::write(const uint8_t* data, uint16_t length)
return RSN_OK; return RSN_OK;
} }
#endif // defined(ENABLE_NXDN)

@ -1,66 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-only
/** /**
* Digital Voice Modem - Hotspot Firmware * Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2018 Andy Uribe, CA6JAU * @package DVM / DSP Firmware (Hotspot)
* Copyright (C) 2020 Jonathan Naylor, G4KLX
* *
*/ */
/** //
* @file CalNXDN.h // Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
* @ingroup nxdn_hfw // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* @file CalNXDN.cpp //
* @ingroup nxdn_hfw /*
* Copyright (C) 2018 by Andy Uribe CA6JAU
* 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(__CAL_NXDN_H__) #if !defined(__CAL_NXDN_H__)
#define __CAL_NXDN_H__ #define __CAL_NXDN_H__
#include "Defines.h" #include "Defines.h"
#include "nxdn/NXDNDefines.h" #include "nxdn/NXDNDefines.h"
#if defined(ENABLE_NXDN)
namespace nxdn namespace nxdn
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Calibration States
* @ingroup nxdn_hfw
*/
enum NXDNCAL1K { enum NXDNCAL1K {
NXDNCAL1K_IDLE, //! Idle NXDNCAL1K_IDLE,
NXDNCAL1K_TX //! Transmit NXDNCAL1K_TX
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements logic for NXDN calibration mode.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements logic for NXDN calibration mode.
* @ingroup nxdn_hfw
*/
class DSP_FW_API CalNXDN { class DSP_FW_API CalNXDN {
public: public:
/** /// <summary>Initializes a new instance of the CalNXDN class.</summary>
* @brief Initializes a new instance of the CalNXDN class.
*/
CalNXDN(); CalNXDN();
/** /// <summary>Process local state and transmit on the air interface.</summary>
* @brief Process local state and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write NXDN calibration state.</summary>
* @brief Write NXDN calibration state.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t write(const uint8_t* data, uint16_t length); uint8_t write(const uint8_t* data, uint16_t length);
private: private:
@ -71,4 +72,6 @@ namespace nxdn
}; };
} // namespace nxdn } // namespace nxdn
#endif // defined(ENABLE_NXDN)
#endif // __CAL_NXDN_H__ #endif // __CAL_NXDN_H__

@ -1,19 +1,31 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2016,2017,2018 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
* @defgroup nxdn_hfw Next Generation Digital Narrowband * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* @brief Implementation for the NXDN standard. * GNU General Public License for more details.
* @ingroup hotspot_fw
* *
* @file NXDNDefines.h * You should have received a copy of the GNU General Public License
* @ingroup nxdn_hfw * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__NXDN_DEFINES_H__) #if !defined(__NXDN_DEFINES_H__)
#define __NXDN_DEFINES_H__ #define __NXDN_DEFINES_H__
@ -26,11 +38,6 @@ namespace nxdn
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @addtogroup nxdn_hfw
* @{
*/
const uint32_t NXDN_RADIO_SYMBOL_LENGTH = 10U; // At 24 kHz sample rate const uint32_t NXDN_RADIO_SYMBOL_LENGTH = 10U; // At 24 kHz sample rate
const uint32_t NXDN_FRAME_LENGTH_BITS = 384U; const uint32_t NXDN_FRAME_LENGTH_BITS = 384U;
@ -61,10 +68,7 @@ namespace nxdn
const uint16_t NXDN_FSW_SYMBOLS = 0x014DU; const uint16_t NXDN_FSW_SYMBOLS = 0x014DU;
const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU; const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU;
// 538 = NXDN_FRAME_LENGTH_BYTES * 11 + 10 (BUFFER_LEN = NXDN_FRAME_LENGTH_BYTES * NO_OF_FRAMES) const uint32_t NXDN_TX_BUFFER_LEN = 1018U; // 2026 = NXDN_FRAME_LENGTH_BYTES * 21 + 10 (BUFFER_LEN = NXDN_FRAME_LENGTH_BYTES * NO_OF_FRAMES)
const uint32_t NXDN_TX_BUFFER_LEN = 538U; // 11 frames + pad
/** @} */
} // namespace nxdn } // namespace nxdn
#endif // __NXDN_DEFINES_H__ #endif // __NXDN_DEFINES_H__

@ -1,12 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2022 Bryan Biedenkapp N2PLL
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2009-2018,2020 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "nxdn/NXDNRX.h" #include "nxdn/NXDNRX.h"
@ -14,6 +34,8 @@
using namespace nxdn; using namespace nxdn;
#if defined(ENABLE_NXDN)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -29,13 +51,15 @@ const uint16_t NOENDPTR = 9999U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the NXDNRX class. */ /// <summary>
/// Initializes a new instance of the NXDNRX class.
/// </summary>
NXDNRX::NXDNRX() : NXDNRX::NXDNRX() :
m_bitBuffer(0x00U), m_bitBuffer(0x00U),
m_outBuffer(), m_outBuffer(),
m_buffer(NULL), m_buffer(NULL),
m_dataPtr(0U), m_dataPtr(0U),
m_endPtr(NOENDPTR),
m_lostCount(0U), m_lostCount(0U),
m_state(NXDNRXS_NONE) m_state(NXDNRXS_NONE)
{ {
@ -43,32 +67,48 @@ NXDNRX::NXDNRX() :
m_buffer = m_outBuffer + 1U; m_buffer = m_outBuffer + 1U;
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void NXDNRX::reset() void NXDNRX::reset()
{ {
m_bitBuffer = 0x00U; m_bitBuffer = 0x00U;
m_dataPtr = 0U; m_dataPtr = 0U;
m_endPtr = NOENDPTR;
m_lostCount = 0U; m_lostCount = 0U;
m_state = NXDNRXS_NONE; m_state = NXDNRXS_NONE;
} }
/* Sample NXDN bits from the air interface. */ /// <summary>
/// Sample NXDN bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void NXDNRX::databit(bool bit) void NXDNRX::databit(bool bit)
{ {
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
if (m_state != NXDNRXS_NONE) {
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_dataPtr++;
if (m_dataPtr > NXDN_FRAME_LENGTH_BITS) {
reset();
}
}
if (m_state == NXDNRXS_DATA) { if (m_state == NXDNRXS_DATA) {
processData(bit); processData(bit);
} }
else { else {
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
bool ret = correlateSync(true); bool ret = correlateSync(true);
if (ret) { if (ret) {
DEBUG3("NXDNRX: databit(): dataPtr/endPtr", m_dataPtr, m_endPtr);
m_state = NXDNRXS_DATA; m_state = NXDNRXS_DATA;
} }
@ -80,35 +120,24 @@ void NXDNRX::databit(bool bit)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to process NXDN data bits. */ /// <summary>
/// Helper to process NXDN data bits.
/// </summary>
/// <param name="bit"></param>
void NXDNRX::processData(bool bit) void NXDNRX::processData(bool bit)
{ {
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_dataPtr++;
if (m_dataPtr > NXDN_FRAME_LENGTH_BITS) {
reset();
}
// only search for a sync in the right place +-2 bits // only search for a sync in the right place +-2 bits
if (m_dataPtr >= (NXDN_FSW_LENGTH_BITS - 2U) && m_dataPtr <= (NXDN_FSW_LENGTH_BITS + 2U)) { if (m_dataPtr >= (NXDN_FSW_LENGTH_BITS - 2U) && m_dataPtr <= (NXDN_FSW_LENGTH_BITS + 2U)) {
if (correlateSync()) { correlateSync();
DEBUG2("NXDNRX::processData() sync found pos", m_dataPtr - NXDN_FSW_LENGTH_BITS);
}
} }
// process frame // process frame
if (m_dataPtr == NXDN_FRAME_LENGTH_BITS) { if (m_dataPtr == m_endPtr) {
m_lostCount--; m_lostCount--;
// we've not seen a data sync for too long, signal sync lost and change to NXDNRXS_NONE // we've not seen a data sync for too long, signal sync lost and change to NXDNRXS_NONE
if (m_lostCount == 0U) { if (m_lostCount == 0U) {
DEBUG1("NXDNRX::processData() sync timed out, lost lock"); DEBUG1("NXDNRX: processData(): sync timed out, lost lock");
io.setDecode(false); io.setDecode(false);
@ -116,6 +145,8 @@ void NXDNRX::processData(bool bit)
reset(); reset();
} }
else { else {
DEBUG2("NXDNRX: processData(): sync found pos", m_dataPtr);
m_outBuffer[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; // set sync flag m_outBuffer[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; // set sync flag
serial.writeNXDNData(m_outBuffer, NXDN_FRAME_LENGTH_BYTES + 1U); serial.writeNXDNData(m_outBuffer, NXDN_FRAME_LENGTH_BYTES + 1U);
@ -125,8 +156,11 @@ void NXDNRX::processData(bool bit)
} }
} }
/* Frame synchronization correlator. */ /// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <param name="first"></param>
/// <returns></returns>
bool NXDNRX::correlateSync(bool first) bool NXDNRX::correlateSync(bool first)
{ {
uint8_t maxErrs; uint8_t maxErrs;
@ -138,7 +172,7 @@ bool NXDNRX::correlateSync(bool first)
// fuzzy matching of the data sync bit sequence // fuzzy matching of the data sync bit sequence
uint8_t errs = countBits64((m_bitBuffer & NXDN_FSW_BITS_MASK) ^ NXDN_FSW_BITS); uint8_t errs = countBits64((m_bitBuffer & NXDN_FSW_BITS_MASK) ^ NXDN_FSW_BITS);
if (errs <= maxErrs) { if (errs <= maxErrs) {
DEBUG2("NXDNRX::correlateSync() sync errs", errs); DEBUG2("NXDNRX: correlateSync(): correlateSync errs", errs);
if (first) { if (first) {
// unpack sync bytes // unpack sync bytes
@ -147,19 +181,22 @@ bool NXDNRX::correlateSync(bool first)
sync[1U] = (uint8_t)((m_bitBuffer >> 8) & NXDN_FSW_BYTES_MASK[1U]); sync[1U] = (uint8_t)((m_bitBuffer >> 8) & NXDN_FSW_BYTES_MASK[1U]);
sync[2U] = (uint8_t)((m_bitBuffer >> 0) & NXDN_FSW_BYTES_MASK[2U]); sync[2U] = (uint8_t)((m_bitBuffer >> 0) & NXDN_FSW_BYTES_MASK[2U]);
DEBUG4("NXDNRX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++)
m_buffer[i] = sync[i]; m_buffer[i] = sync[i];
DEBUG4("NXDNRX::correlateSync() sync [b0 - b2]", m_buffer[0], m_buffer[1], m_buffer[2]);
} }
m_lostCount = MAX_FSW_FRAMES; m_lostCount = MAX_FSW_FRAMES;
m_dataPtr = NXDN_FSW_LENGTH_BITS; m_dataPtr = NXDN_FSW_LENGTH_BITS;
m_endPtr = NXDN_FRAME_LENGTH_BITS;
DEBUG2("NXDNRX::correlateSync() dataPtr", m_dataPtr - NXDN_FSW_LENGTH_BITS); DEBUG3("NXDNRX: correlateSync(): dataPtr/endPtr", m_dataPtr, m_endPtr);
return true; return true;
} }
return false; return false;
} }
#endif // defined(ENABLE_NXDN)

@ -1,18 +1,32 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2022 Bryan Biedenkapp N2PLL
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2015,2016,2017,2018,2020 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * 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
* @file NXDNRX.h * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* @ingroup nxdn_hfw * GNU General Public License for more details.
* @file NXDNRX.cpp *
* @ingroup nxdn_hfw * 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(__NXDN_RX_H__) #if !defined(__NXDN_RX_H__)
#define __NXDN_RX_H__ #define __NXDN_RX_H__
@ -20,45 +34,32 @@
#include "Defines.h" #include "Defines.h"
#include "nxdn/NXDNDefines.h" #include "nxdn/NXDNDefines.h"
#if defined(ENABLE_NXDN)
namespace nxdn namespace nxdn
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief NXDN Receiver State
* @ingroup nxdn_hfw
*/
enum NXDNRX_STATE { enum NXDNRX_STATE {
NXDNRXS_NONE, //! None NXDNRXS_NONE,
NXDNRXS_DATA //! Data NXDNRXS_DATA
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for DMR slots.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for NXDN mode operation.
* @ingroup nxdn_mfw
*/
class DSP_FW_API NXDNRX { class DSP_FW_API NXDNRX {
public: public:
/** /// <summary>Initializes a new instance of the NXDNRX class.</summary>
* @brief Initializes a new instance of the NXDNRX class.
*/
NXDNRX(); NXDNRX();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample NXDN bits from the air interface.</summary>
* @brief Sample NXDN bits from the air interface.
* @param bit
*/
void databit(bool bit); void databit(bool bit);
private: private:
@ -68,22 +69,19 @@ namespace nxdn
uint16_t m_dataPtr; uint16_t m_dataPtr;
uint16_t m_endPtr;
uint16_t m_lostCount; uint16_t m_lostCount;
NXDNRX_STATE m_state; NXDNRX_STATE m_state;
/** /// <summary>Helper to process NXDN data bits.</summary>
* @brief Helper to process NXDN data bits.
* @param bit
*/
void processData(bool bit); void processData(bool bit);
/** /// <summary>Frame synchronization correlator.</summary>
* @brief Frame synchronization correlator.
* @param first
*/
bool correlateSync(bool first = false); bool correlateSync(bool first = false);
}; };
} // namespace nxdn } // namespace nxdn
#endif // defined(ENABLE_NXDN)
#endif // __NXDN_RX_H__ #endif // __NXDN_RX_H__

@ -1,13 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2017 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2022 by Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2009-2018,2020 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2017 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "nxdn/NXDNTX.h" #include "nxdn/NXDNTX.h"
@ -15,12 +35,15 @@
using namespace nxdn; using namespace nxdn;
#if defined(ENABLE_NXDN)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the NXDNTX class. */ /// <summary>
/// Initializes a new instance of the NXDNTX class.
/// </summary>
NXDNTX::NXDNTX() : NXDNTX::NXDNTX() :
m_fifo(NXDN_TX_BUFFER_LEN), m_fifo(NXDN_TX_BUFFER_LEN),
m_state(NXDNTXSTATE_NORMAL), m_state(NXDNTXSTATE_NORMAL),
@ -34,8 +57,9 @@ NXDNTX::NXDNTX() :
/* stub */ /* stub */
} }
/* Process local buffer and transmit on the air interface. */ /// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void NXDNTX::process() void NXDNTX::process()
{ {
if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U && if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U &&
@ -69,6 +93,8 @@ void NXDNTX::process()
return; return;
createData(); createData();
DEBUG2("NXDNTX: process(): poLen", m_poLen);
} }
if (m_poLen > 0U) { if (m_poLen > 0U) {
@ -91,15 +117,19 @@ void NXDNTX::process()
} }
} }
/* Write data to the local buffer. */ /// <summary>
/// Write data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t NXDNTX::writeData(const uint8_t* data, uint16_t length) uint8_t NXDNTX::writeData(const uint8_t* data, uint16_t length)
{ {
if (length != (NXDN_FRAME_LENGTH_BYTES + 1U)) if (length != (NXDN_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace(); uint16_t space = m_fifo.getSpace();
DEBUG3("NXDNTX::writeData() dataLength/fifoLength", length, space); DEBUG3("NXDNTX: writeData(): dataLength/fifoLength", length, space);
if (space < NXDN_FRAME_LENGTH_BYTES) if (space < NXDN_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL; return RSN_RINGBUFF_FULL;
@ -109,48 +139,48 @@ uint8_t NXDNTX::writeData(const uint8_t* data, uint16_t length)
return RSN_OK; return RSN_OK;
} }
/* Clears the local buffer. */ /// <summary>
/// Clears the local buffer.
/// </summary>
void NXDNTX::clear() void NXDNTX::clear()
{ {
m_fifo.reset(); m_fifo.reset();
} }
/* Sets the FDMA preamble count. */ /// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void NXDNTX::setPreambleCount(uint8_t preambleCnt) void NXDNTX::setPreambleCount(uint8_t preambleCnt)
{ {
m_preambleCnt = 300U + uint16_t(preambleCnt) * 6U; // 500ms + tx delay m_preambleCnt = 300U + uint16_t(preambleCnt) * 6U; // 500ms + tx delay
// clamp preamble count if (m_preambleCnt > 1200U)
if (m_preambleCnt > 60U) m_preambleCnt = 1200U;
m_preambleCnt = 60U;
} }
/* Sets the Tx hang time. */ /// <summary>
/// Sets the Tx hang time.
/// </summary>
/// <param name="txHang">Transmit hang time in seconds.</param>
void NXDNTX::setTxHang(uint8_t txHang) void NXDNTX::setTxHang(uint8_t txHang)
{ {
m_txHang = txHang * NXDN_FIXED_TX_HANG; m_txHang = txHang * NXDN_FIXED_TX_HANG;
} }
/* Helper to set the calibration state for Tx. */ /// <summary>
/// Helper to set the calibration state for Tx.
/// </summary>
/// <param name="start"></param>
void NXDNTX::setCal(bool start) void NXDNTX::setCal(bool start)
{ {
m_state = start ? NXDNTXSTATE_CAL : NXDNTXSTATE_NORMAL; m_state = start ? NXDNTXSTATE_CAL : NXDNTXSTATE_NORMAL;
} }
/* Helper to resize the FIFO buffer. */ /// <summary>
/// Helper to get how much space the ring buffer has for samples.
void NXDNTX::resizeBuffer(uint16_t size) /// </summary>
{ /// <returns></returns>
m_fifo.reset();
m_fifo.reinitialize(size);
}
/* Helper to get how much space the ring buffer has for samples. */
uint8_t NXDNTX::getSpace() const uint8_t NXDNTX::getSpace() const
{ {
return m_fifo.getSpace() / NXDN_FRAME_LENGTH_BYTES; return m_fifo.getSpace() / NXDN_FRAME_LENGTH_BYTES;
@ -160,8 +190,9 @@ uint8_t NXDNTX::getSpace() const
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to generate data. */ /// <summary>
///
/// </summary>
void NXDNTX::createData() void NXDNTX::createData()
{ {
if (!m_tx) { if (!m_tx) {
@ -173,7 +204,7 @@ void NXDNTX::createData()
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U]; m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U];
} }
else { else {
DEBUG2("NXDNTX::createData() fifoSpace", m_fifo.getSpace()); DEBUG2("NXDNTX: createData(): fifoSpace", m_fifo.getSpace());
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[m_poLen++] = m_fifo.get(); m_poBuffer[m_poLen++] = m_fifo.get();
} }
@ -182,8 +213,10 @@ void NXDNTX::createData()
m_poPtr = 0U; m_poPtr = 0U;
} }
/* Helper to write a raw byte to the DAC. */ /// <summary>
///
/// </summary>
/// <param name="c"></param>
void NXDNTX::writeByte(uint8_t c) void NXDNTX::writeByte(uint8_t c)
{ {
uint8_t bit; uint8_t bit;
@ -199,8 +232,9 @@ void NXDNTX::writeByte(uint8_t c)
} }
} }
/* */ /// <summary>
///
/// </summary>
void NXDNTX::writeSilence() void NXDNTX::writeSilence()
{ {
uint8_t bit; uint8_t bit;
@ -209,3 +243,5 @@ void NXDNTX::writeSilence()
io.write(&bit, 1); io.write(&bit, 1);
} }
} }
#endif // defined(ENABLE_NXDN)

@ -1,25 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2022 by Bryan Biedenkapp N2PLL
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2015,2016,2017,2018,2020 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2022 Bryan Biedenkapp, N2PLL * 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.
*/ */
/**
* @file NXDNTX.h
* @ingroup nxdn_hfw
* @file NXDNTX.cpp
* @ingroup nxdn_hfw
*/
#if !defined(__NXDN_TX_H__) #if !defined(__NXDN_TX_H__)
#define __NXDN_TX_H__ #define __NXDN_TX_H__
#include "Defines.h" #include "Defines.h"
#include "SerialBuffer.h" #include "SerialBuffer.h"
#if defined(ENABLE_NXDN)
namespace nxdn namespace nxdn
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -28,74 +45,38 @@ namespace nxdn
#define NXDN_FIXED_TX_HANG 600 #define NXDN_FIXED_TX_HANG 600
/**
* @brief NXDN Transmitter States
* @ingroup nxdn_hfw
*/
enum NXDNTXSTATE { enum NXDNTXSTATE {
NXDNTXSTATE_NORMAL, //! Normal NXDNTXSTATE_NORMAL,
NXDNTXSTATE_CAL //! Calibration NXDNTXSTATE_CAL
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements transmitter logic for NXDN mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements transmitter logic for NXDN mode operation.
* @ingroup nxdn_hfw
*/
class DSP_FW_API NXDNTX { class DSP_FW_API NXDNTX {
public: public:
/** /// <summary>Initializes a new instance of the NXDNTX class.</summary>
* @brief Initializes a new instance of the NXDNTX class.
*/
NXDNTX(); NXDNTX();
/** /// <summary>Process local buffer and transmit on the air interface.</summary>
* @brief Process local buffer and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write data to the local buffer.</summary>
* @brief Write data to the local buffer.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t writeData(const uint8_t* data, uint16_t length); uint8_t writeData(const uint8_t* data, uint16_t length);
/** /// <summary>Clears the local buffer.</summary>
* @brief Clears the local buffer.
*/
void clear(); void clear();
/** /// <summary>Sets the FDMA preamble count.</summary>
* @brief Sets the FDMA preamble count.
* @param preambleCnt FDMA preamble count.
*/
void setPreambleCount(uint8_t preambleCnt); void setPreambleCount(uint8_t preambleCnt);
/** /// <summary>Sets the transmit hang time.</summary>
* @brief Sets the transmit hang time.
* @param txHang Transmit hang time.
*/
void setTxHang(uint8_t txHang); void setTxHang(uint8_t txHang);
/** /// <summary>Helper to set the calibration state for Tx.</summary>
* @brief Helper to set the calibration state for Tx.
* @param start
*/
void setCal(bool start); void setCal(bool start);
/** /// <summary>Helper to get how much space the ring buffer has for samples.</summary>
* @brief Helper to resize the FIFO buffer.
* @param size
*/
void resizeBuffer(uint16_t size);
/**
* @brief Helper to get how much space the ring buffer has for samples.
* @returns uint8_t Amount of space in ring buffer for samples.
*/
uint8_t getSpace() const; uint8_t getSpace() const;
private: private:
@ -111,21 +92,16 @@ namespace nxdn
uint32_t m_txHang; uint32_t m_txHang;
uint32_t m_tailCnt; uint32_t m_tailCnt;
/** /// <summary></summary>
* @brief Helper to generate data.
*/
void createData(); void createData();
/** /// <summary></summary>
* @brief Helper to write a raw byte to the DAC.
* @param c Byte.
*/
void writeByte(uint8_t c); void writeByte(uint8_t c);
/** /// <summary></summary>
* @brief
*/
void writeSilence(); void writeSilence();
}; };
} // namespace nxdn } // namespace nxdn
#endif // defined(ENABLE_NXDN)
#endif // __NXDN_TX_H__ #endif // __NXDN_TX_H__

@ -1,17 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2018 by Andy Uribe CA6JAU
* GPLv2 Open Source. Use is subject to license terms. *
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * 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
* Copyright (C) 2018 Andy Uribe, CA6JAU * 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 "Globals.h" #include "Globals.h"
#include "p25/CalP25.h" #include "p25/CalP25.h"
using namespace p25; using namespace p25;
#if defined(ENABLE_P25)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -50,8 +72,9 @@ unsigned char LDU2_1K[] = { 0x00,
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the CalP25 class. */ /// <summary>
/// Initializes a new instance of the CalP25 class.
/// </summary>
CalP25::CalP25() : CalP25::CalP25() :
m_transmit(false), m_transmit(false),
m_state(P25CAL1K_IDLE) m_state(P25CAL1K_IDLE)
@ -59,8 +82,9 @@ CalP25::CalP25() :
/* stub */ /* stub */
} }
/* Process local state and transmit on the air interface. */ /// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalP25::process() void CalP25::process()
{ {
if (m_modemState == STATE_P25_CAL) { if (m_modemState == STATE_P25_CAL) {
@ -100,8 +124,12 @@ void CalP25::process()
} }
} }
/* Write P25 calibration data to the local buffer. */ /// <summary>
/// Write P25 calibration data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalP25::write(const uint8_t* data, uint8_t length) uint8_t CalP25::write(const uint8_t* data, uint8_t length)
{ {
if (length != 1U) if (length != 1U)
@ -117,3 +145,5 @@ uint8_t CalP25::write(const uint8_t* data, uint8_t length)
return RSN_OK; return RSN_OK;
} }
#endif // defined(ENABLE_P25)

@ -1,66 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/** /**
* Digital Voice Modem - Hotspot Firmware * Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2018 Andy Uribe, CA6JAU * @package DVM / DSP Firmware (Hotspot)
* *
*/ */
/** //
* @file CalP25.h // Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
* @ingroup p25_hfw // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
* @file CalP25.cpp //
* @ingroup p25_hfw /*
*/ * Copyright (C) 2018 by Andy Uribe CA6JAU
*
* 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(__CAL_P25_H__) #if !defined(__CAL_P25_H__)
#define __CAL_P25_H__ #define __CAL_P25_H__
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#if defined(ENABLE_P25)
namespace p25 namespace p25
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Calibration States
* @ingroup p25_hfw
*/
enum P25CAL1K { enum P25CAL1K {
P25CAL1K_IDLE, //! Idle P25CAL1K_IDLE,
P25CAL1K_LDU1, //! LDU1 P25CAL1K_LDU1,
P25CAL1K_LDU2 //! LDU2 P25CAL1K_LDU2
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements logic for P25 calibration mode.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements logic for P25 calibration mode.
* @ingroup p25_hfw
*/
class DSP_FW_API CalP25 { class DSP_FW_API CalP25 {
public: public:
/** /// <summary>Initializes a new instance of the CalP25 class.</summary>
* @brief Initializes a new instance of the CalP25 class.
*/
CalP25(); CalP25();
/** /// <summary>Process local state and transmit on the air interface.</summary>
* @brief Process local state and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write P25 calibration state.</summary>
* @brief Write P25 calibration state.
* @param[in] data Buffer.
* @param length Length of buffer.
* @returns uint8_t Reason code.
*/
uint8_t write(const uint8_t* data, uint8_t length); uint8_t write(const uint8_t* data, uint8_t length);
private: private:
@ -69,4 +69,6 @@ namespace p25
}; };
} // namespace p25 } // namespace p25
#endif // defined(ENABLE_P25)
#endif // __CAL_P25_H__ #endif // __CAL_P25_H__

@ -1,21 +1,51 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2016 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2017-2018 Bryan Biedenkapp N2PLL
*
* 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.
*/
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2009-2016 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2018 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2017-2024 Bryan Biedenkapp, N2PLL * 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
* @defgroup p25_hfw Project 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* @brief Implementation for the TIA-102 Project 25 standard. * GNU General Public License for more details.
* @ingroup hotspot_fw
* *
* @file P25Defines.h * You should have received a copy of the GNU General Public License
* @ingroup p25_hfw * along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#if !defined(__P25_DEFINES_H__) #if !defined(__P25_DEFINES_H__)
#define __P25_DEFINES_H__ #define __P25_DEFINES_H__
@ -28,11 +58,6 @@ namespace p25
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @addtogroup p25_hfw
* @{
*/
const uint32_t P25_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate const uint32_t P25_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
const uint32_t P25_HDU_FRAME_LENGTH_BYTES = 99U; const uint32_t P25_HDU_FRAME_LENGTH_BYTES = 99U;
@ -55,11 +80,6 @@ namespace p25
const uint32_t P25_TSDU_FRAME_LENGTH_SYMBOLS = P25_TSDU_FRAME_LENGTH_BYTES * 4U; const uint32_t P25_TSDU_FRAME_LENGTH_SYMBOLS = P25_TSDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_TSDU_FRAME_LENGTH_SAMPLES = P25_TSDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH; const uint32_t P25_TSDU_FRAME_LENGTH_SAMPLES = P25_TSDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_PDU_FRAME_LENGTH_BYTES = 512U;
const uint32_t P25_PDU_FRAME_LENGTH_BITS = P25_PDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_PDU_FRAME_LENGTH_SYMBOLS = P25_PDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_PDU_FRAME_LENGTH_SAMPLES = P25_PDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_TDULC_FRAME_LENGTH_BYTES = 54U; const uint32_t P25_TDULC_FRAME_LENGTH_BYTES = 54U;
const uint32_t P25_TDULC_FRAME_LENGTH_BITS = P25_TDULC_FRAME_LENGTH_BYTES * 8U; const uint32_t P25_TDULC_FRAME_LENGTH_BITS = P25_TDULC_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_TDULC_FRAME_LENGTH_SYMBOLS = P25_TDULC_FRAME_LENGTH_BYTES * 4U; const uint32_t P25_TDULC_FRAME_LENGTH_SYMBOLS = P25_TDULC_FRAME_LENGTH_BYTES * 4U;
@ -75,6 +95,11 @@ namespace p25
const uint32_t P25_NID_LENGTH_SYMBOLS = P25_NID_LENGTH_BYTES * 4U; const uint32_t P25_NID_LENGTH_SYMBOLS = P25_NID_LENGTH_BYTES * 4U;
const uint32_t P25_NID_LENGTH_SAMPLES = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH; const uint32_t P25_NID_LENGTH_SAMPLES = P25_NID_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_BYTES = 39U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_BITS = P25_PDU_HDU_FRAME_LENGTH_BYTES * 8U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_SYMBOLS = P25_PDU_HDU_FRAME_LENGTH_BYTES * 4U;
const uint32_t P25_PDU_HDU_FRAME_LENGTH_SAMPLES = P25_PDU_HDU_FRAME_LENGTH_SYMBOLS * P25_RADIO_SYMBOL_LENGTH;
const uint8_t P25_SYNC_BYTES[] = { 0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU }; const uint8_t P25_SYNC_BYTES[] = { 0x55U, 0x75U, 0xF5U, 0xFFU, 0x77U, 0xFFU };
const uint8_t P25_SYNC_BYTES_LENGTH = 6U; const uint8_t P25_SYNC_BYTES_LENGTH = 6U;
const uint8_t P25_START_SYNC = 0x5FU; const uint8_t P25_START_SYNC = 0x5FU;
@ -95,21 +120,16 @@ namespace p25
const uint32_t P25_SYNC_SYMBOLS = 0x00FB30A0U; const uint32_t P25_SYNC_SYMBOLS = 0x00FB30A0U;
const uint32_t P25_SYNC_SYMBOLS_MASK = 0x00FFFFFFU; const uint32_t P25_SYNC_SYMBOLS_MASK = 0x00FFFFFFU;
// 522 = P25_PDU_FRAME_LENGTH_BYTES + 10 (BUFFER_LEN = P25_PDU_FRAME_LENGTH_BYTES + 10) const uint32_t P25_TX_BUFFER_LEN = 1738U; // 2592 = P25_LDU_FRAME_LENGTH_BYTES * 8 + 10 (BUFFER_LEN = P25_LDU_FRAME_LENGTH_BYTES * NO_OF_FRAMES + 10)
const uint32_t P25_TX_BUFFER_LEN = 522U;
// Data Unit ID(s) // Data Unit ID(s)
const uint8_t P25_DUID_HDU = 0x00U; // Header Data Unit const uint8_t P25_DUID_HDU = 0x00U; // Header Data Unit
const uint8_t P25_DUID_TDU = 0x03U; // Simple Terminator Data Unit const uint8_t P25_DUID_TDU = 0x03U; // Simple Terminator Data Unit
const uint8_t P25_DUID_LDU1 = 0x05U; // Logical Link Data Unit 1 const uint8_t P25_DUID_LDU1 = 0x05U; // Logical Link Data Unit 1
const uint8_t P25_DUID_VSELP1 = 0x06U; // Motorola VSELP 1
const uint8_t P25_DUID_TSDU = 0x07U; // Trunking System Data Unit const uint8_t P25_DUID_TSDU = 0x07U; // Trunking System Data Unit
const uint8_t P25_DUID_VSELP2 = 0x09U; // Motorola VSELP 2
const uint8_t P25_DUID_LDU2 = 0x0AU; // Logical Link Data Unit 2 const uint8_t P25_DUID_LDU2 = 0x0AU; // Logical Link Data Unit 2
const uint8_t P25_DUID_PDU = 0x0CU; // Packet Data Unit const uint8_t P25_DUID_PDU = 0x0CU; // Packet Data Unit
const uint8_t P25_DUID_TDULC = 0x0FU; // Terminator Data Unit with Link Control const uint8_t P25_DUID_TDULC = 0x0FU; // Terminator Data Unit with Link Control
/** @} */
} // namespace p25 } // namespace p25
#endif // __P25_DEFINES_H__ #endif // __P25_DEFINES_H__

@ -1,20 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021 Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021-2024 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "p25/P25RX.h" #include "p25/P25RX.h"
#include "Utils.h" #include "Utils.h"
using namespace p25; using namespace p25;
#if defined(ENABLE_P25)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -30,14 +52,14 @@ const uint16_t NOENDPTR = 9999U;
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the P25RX class. */ /// <summary>
/// Initializes a new instance of the P25RX class.
/// </summary>
P25RX::P25RX() : P25RX::P25RX() :
m_bitBuffer(0x00U), m_bitBuffer(0x00U),
m_buffer(), m_buffer(),
m_dataPtr(0U), m_dataPtr(0U),
m_endPtr(NOENDPTR), m_endPtr(NOENDPTR),
m_pduEndPtr(NOENDPTR),
m_lostCount(0U), m_lostCount(0U),
m_nac(0xF7EU), m_nac(0xF7EU),
m_state(P25RXS_NONE), m_state(P25RXS_NONE),
@ -46,8 +68,9 @@ P25RX::P25RX() :
::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U); ::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U);
} }
/* Helper to reset data values to defaults. */ /// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void P25RX::reset() void P25RX::reset()
{ {
m_bitBuffer = 0x00U; m_bitBuffer = 0x00U;
@ -56,7 +79,6 @@ void P25RX::reset()
::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U); ::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U);
m_endPtr = NOENDPTR; m_endPtr = NOENDPTR;
m_pduEndPtr = NOENDPTR;
m_lostCount = 0U; m_lostCount = 0U;
@ -65,8 +87,10 @@ void P25RX::reset()
m_duid = 0xFFU; m_duid = 0xFFU;
} }
/* Sample P25 bits from the air interface. */ /// <summary>
/// Sample P25 bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void P25RX::databit(bool bit) void P25RX::databit(bool bit)
{ {
m_bitBuffer <<= 1; m_bitBuffer <<= 1;
@ -77,15 +101,9 @@ void P25RX::databit(bool bit)
_WRITE_BIT(m_buffer, m_dataPtr, bit); _WRITE_BIT(m_buffer, m_dataPtr, bit);
m_dataPtr++; m_dataPtr++;
if (m_state != P25RXS_DATA) {
if (m_dataPtr > P25_LDU_FRAME_LENGTH_BITS) { if (m_dataPtr > P25_LDU_FRAME_LENGTH_BITS) {
reset(); reset();
} }
} else {
if (m_dataPtr > P25_PDU_FRAME_LENGTH_BITS) {
reset();
}
}
} }
if (m_state == P25RXS_SYNC) { if (m_state == P25RXS_SYNC) {
@ -108,8 +126,10 @@ void P25RX::databit(bool bit)
} }
} }
/* Sets the P25 NAC. */ /// <summary>
/// Sets the P25 NAC.
/// </summary>
/// <param name="nac">NAC.</param>
void P25RX::setNAC(uint16_t nac) void P25RX::setNAC(uint16_t nac)
{ {
m_nac = nac; m_nac = nac;
@ -119,13 +139,15 @@ void P25RX::setNAC(uint16_t nac)
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to process P25 samples. */ /// <summary>
/// Helper to process P25 samples.
/// </summary>
/// <param name="bit"></param>
void P25RX::processBit(bool bit) void P25RX::processBit(bool bit)
{ {
// process NID // process NID
if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) { if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) {
DEBUG3("P25RX::processBit() dataPtr/endPtr", m_dataPtr, m_endPtr); DEBUG3("P25RX: processBit(): dataPtr/endPtr", m_dataPtr, m_endPtr);
if (!decodeNid()) { if (!decodeNid()) {
io.setDecode(false); io.setDecode(false);
@ -137,45 +159,43 @@ void P25RX::processBit(bool bit)
switch (m_duid) { switch (m_duid) {
case P25_DUID_HDU: case P25_DUID_HDU:
{ {
DEBUG2("P25RX::processBit() sync found in HDU pos", m_dataPtr); DEBUG2("P25RX: processBit(): sync found in HDU pos", m_dataPtr);
m_endPtr = P25_HDU_FRAME_LENGTH_BITS; m_endPtr = P25_HDU_FRAME_LENGTH_BITS;
} }
break; break;
case P25_DUID_TDU: case P25_DUID_TDU:
{ {
DEBUG2("P25RX::processBit() sync found in TDU pos", m_dataPtr); DEBUG2("P25RX: processBit(): sync found in TDU pos", m_dataPtr);
m_endPtr = P25_TDU_FRAME_LENGTH_BITS; m_endPtr = P25_TDU_FRAME_LENGTH_BITS;
} }
break; break;
case P25_DUID_LDU1: case P25_DUID_LDU1:
case P25_DUID_VSELP1:
m_state = P25RXS_VOICE; m_state = P25RXS_VOICE;
m_endPtr = P25_LDU_FRAME_LENGTH_BITS; m_endPtr = P25_LDU_FRAME_LENGTH_BITS;
return; return;
case P25_DUID_TSDU: case P25_DUID_TSDU:
{ {
DEBUG2("P25RX::processBit() sync found in TSDU pos", m_dataPtr); DEBUG2("P25RX: processBit(): sync found in TSDU pos", m_dataPtr);
m_endPtr = P25_TSDU_FRAME_LENGTH_BITS; m_endPtr = P25_TSDU_FRAME_LENGTH_BITS;
} }
break; break;
case P25_DUID_LDU2: case P25_DUID_LDU2:
case P25_DUID_VSELP2:
m_state = P25RXS_VOICE; m_state = P25RXS_VOICE;
m_endPtr = P25_LDU_FRAME_LENGTH_BITS; m_endPtr = P25_LDU_FRAME_LENGTH_BITS;
return; return;
case P25_DUID_PDU: case P25_DUID_PDU:
m_state = P25RXS_DATA; m_state = P25RXS_DATA;
m_endPtr = m_pduEndPtr = P25_PDU_FRAME_LENGTH_BITS; m_endPtr = P25_LDU_FRAME_LENGTH_BITS;
return; return;
case P25_DUID_TDULC: case P25_DUID_TDULC:
{ {
DEBUG2("P25RX::processBit() sync found in TDULC pos", m_dataPtr); DEBUG2("P25RX: processBit(): sync found in TDULC pos", m_dataPtr);
m_endPtr = P25_TDULC_FRAME_LENGTH_BITS; m_endPtr = P25_TDULC_FRAME_LENGTH_BITS;
} }
break; break;
default: default:
{ {
DEBUG3("P25RX::processBit() illegal DUID in NID", m_nac, m_duid); DEBUG3("P25RX: processBit(): illegal DUID in NID", m_nac, m_duid);
reset(); reset();
} }
return; return;
@ -183,10 +203,14 @@ void P25RX::processBit(bool bit)
} }
} }
if (m_state == P25RXS_SYNC) { if (m_state == P25RXS_VOICE) {
// only search for a sync in the right place +-2 bits m_lostCount = MAX_SYNC_FRAMES;
if (m_dataPtr >= (P25_SYNC_LENGTH_BITS - 2U) && m_dataPtr <= (P25_SYNC_LENGTH_BITS + 2U)) { processVoice(bit);
correlateSync(); }
if (m_state == P25RXS_DATA) {
m_lostCount = MAX_SYNC_FRAMES;
processData(bit);
} }
// since we aren't processing voice or data -- simply wait till we've reached the end pointer // since we aren't processing voice or data -- simply wait till we've reached the end pointer
@ -201,22 +225,12 @@ void P25RX::processBit(bool bit)
serial.writeP25Data(frame, (m_endPtr / 8U) + 1U); serial.writeP25Data(frame, (m_endPtr / 8U) + 1U);
reset(); reset();
} }
}
else {
if (m_state == P25RXS_VOICE) {
m_lostCount = MAX_SYNC_FRAMES;
processVoice(bit);
}
if (m_state == P25RXS_DATA) {
m_lostCount = MAX_SYNC_FRAMES;
processData(bit);
}
}
} }
/* Helper to process LDU P25 bits. */ /// <summary>
/// Helper to process LDU P25 bits.
/// </summary>
/// <param name="bit"></param>
void P25RX::processVoice(bool bit) void P25RX::processVoice(bool bit)
{ {
// only search for a sync in the right place +-2 bits // only search for a sync in the right place +-2 bits
@ -226,7 +240,7 @@ void P25RX::processVoice(bool bit)
// process NID // process NID
if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) { if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) {
DEBUG3("P25RX::processVoice() dataPtr/endPtr", m_dataPtr, m_endPtr); DEBUG3("P25RX: processVoice(): dataPtr/endPtr", m_dataPtr, m_endPtr);
if (!decodeNid()) { if (!decodeNid()) {
io.setDecode(false); io.setDecode(false);
@ -238,7 +252,7 @@ void P25RX::processVoice(bool bit)
switch (m_duid) { switch (m_duid) {
case P25_DUID_TDU: case P25_DUID_TDU:
{ {
DEBUG2("P25RX::processVoice() sync found in TDU pos", m_dataPtr); DEBUG2("P25RX: processVoice(): sync found in TDU pos", m_dataPtr);
m_endPtr = P25_TDU_FRAME_LENGTH_BITS; m_endPtr = P25_TDU_FRAME_LENGTH_BITS;
} }
break; break;
@ -250,7 +264,7 @@ void P25RX::processVoice(bool bit)
return; return;
default: default:
{ {
DEBUG3("P25RX::processVoice() illegal DUID in NID", m_nac, m_duid); DEBUG3("P25RX: processVoice(): illegal DUID in NID", m_nac, m_duid);
reset(); reset();
} }
return; return;
@ -261,7 +275,7 @@ void P25RX::processVoice(bool bit)
// if we've reached the end pointer and the DUID is a TDU; send it // if we've reached the end pointer and the DUID is a TDU; send it
if (m_dataPtr == m_endPtr && m_duid == P25_DUID_TDU) if (m_dataPtr == m_endPtr && m_duid == P25_DUID_TDU)
{ {
DEBUG2("P25RX::processVoice() sync found in TDU pos", m_dataPtr); DEBUG2("P25RX: processVoice(): sync found in TDU pos", m_dataPtr);
uint8_t frame[P25_TDU_FRAME_LENGTH_BYTES + 1U]; uint8_t frame[P25_TDU_FRAME_LENGTH_BYTES + 1U];
::memcpy(frame + 1U, m_buffer, m_endPtr / 8U); ::memcpy(frame + 1U, m_buffer, m_endPtr / 8U);
@ -281,7 +295,7 @@ void P25RX::processVoice(bool bit)
// we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE // we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE
if (m_lostCount == 0U) { if (m_lostCount == 0U) {
DEBUG1("P25RX::processVoice() sync timeout in LDU, lost lock"); DEBUG1("P25RX: processVoice(): sync timeout in LDU, lost lock");
io.setDecode(false); io.setDecode(false);
@ -289,7 +303,7 @@ void P25RX::processVoice(bool bit)
reset(); reset();
} }
else { else {
DEBUG2("P25RX::processVoice() sync found in LDU pos", m_dataPtr); DEBUG2("P25RX: processVoice(): sync found in LDU pos", m_dataPtr);
uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 3U]; uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 3U];
::memcpy(frame + 1U, m_buffer, m_endPtr / 8U); ::memcpy(frame + 1U, m_buffer, m_endPtr / 8U);
@ -309,8 +323,10 @@ void P25RX::processVoice(bool bit)
} }
} }
/* Helper to process PDU P25 bits. */ /// <summary>
/// Helper to process PDU P25 bits.
/// </summary>
/// <param name="bit"></param>
void P25RX::processData(bool bit) void P25RX::processData(bool bit)
{ {
// only search for a sync in the right place +-2 bits // only search for a sync in the right place +-2 bits
@ -320,7 +336,7 @@ void P25RX::processData(bool bit)
// process NID // process NID
if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) { if (m_dataPtr == P25_SYNC_LENGTH_BITS + P25_NID_LENGTH_BITS + 1) {
DEBUG3("P25RX::processData() dataPtr/pduEndPtr", m_dataPtr, m_pduEndPtr); DEBUG3("P25RX: processVoice(): dataPtr/endPtr", m_dataPtr, m_endPtr);
if (!decodeNid()) { if (!decodeNid()) {
io.setDecode(false); io.setDecode(false);
@ -331,11 +347,11 @@ void P25RX::processData(bool bit)
else { else {
switch (m_duid) { switch (m_duid) {
case P25_DUID_PDU: case P25_DUID_PDU:
m_endPtr = m_pduEndPtr = P25_PDU_FRAME_LENGTH_BITS; m_endPtr = P25_LDU_FRAME_LENGTH_BITS;
return; return;
default: default:
{ {
DEBUG3("P25RX::processData() illegal DUID in NID", m_nac, m_duid); DEBUG3("P25RX: processData(): illegal DUID in NID", m_nac, m_duid);
reset(); reset();
} }
return; return;
@ -343,13 +359,13 @@ void P25RX::processData(bool bit)
} }
} }
// process data frame // process voice frame
if (m_dataPtr == m_pduEndPtr) { if (m_dataPtr == m_endPtr) {
m_lostCount--; m_lostCount--;
// we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE // we've not seen a data sync for too long, signal sync lost and change to P25RXS_NONE
if (m_lostCount == 0U) { if (m_lostCount == 0U) {
DEBUG1("P25RX::processData() sync timeout in PDU, lost lock"); DEBUG1("P25RX: processData(): sync timeout in PDU, lost lock");
io.setDecode(false); io.setDecode(false);
@ -357,19 +373,21 @@ void P25RX::processData(bool bit)
reset(); reset();
} }
else { else {
DEBUG2("P25RX::processData() sync found in PDU pos", m_dataPtr); DEBUG2("P25RX: processData(): sync found in PDU pos", m_dataPtr);
uint8_t frame[P25_PDU_FRAME_LENGTH_BYTES + 1U]; uint8_t frame[P25_LDU_FRAME_LENGTH_BYTES + 1U];
::memcpy(frame + 1U, m_buffer, m_pduEndPtr / 8U); ::memcpy(frame + 1U, m_buffer, m_endPtr / 8U);
frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U; // set sync flag frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U; // set sync flag
serial.writeP25Data(frame, P25_PDU_FRAME_LENGTH_BYTES + 1U); serial.writeP25Data(frame, P25_LDU_FRAME_LENGTH_BYTES + 1U);
} }
} }
} }
/* Frame synchronization correlator. */ /// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <returns></returns>
bool P25RX::correlateSync() bool P25RX::correlateSync()
{ {
uint8_t maxErrs; uint8_t maxErrs;
@ -383,7 +401,7 @@ bool P25RX::correlateSync()
if (errs <= maxErrs) { if (errs <= maxErrs) {
::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U); ::memset(m_buffer, 0x00U, P25_LDU_FRAME_LENGTH_BYTES + 3U);
DEBUG2("P25RX::correlateSync() sync errs", errs); DEBUG2("P25RX: correlateSync(): correlateSync errs", errs);
// unpack sync bytes // unpack sync bytes
uint8_t sync[P25_SYNC_BYTES_LENGTH]; uint8_t sync[P25_SYNC_BYTES_LENGTH];
@ -394,8 +412,8 @@ bool P25RX::correlateSync()
sync[4U] = (uint8_t)((m_bitBuffer >> 8) & 0xFFU); sync[4U] = (uint8_t)((m_bitBuffer >> 8) & 0xFFU);
sync[5U] = (uint8_t)((m_bitBuffer >> 0) & 0xFFU); sync[5U] = (uint8_t)((m_bitBuffer >> 0) & 0xFFU);
DEBUG4("P25RX::correlateSync() sync [b0 - b2]", sync[0], sync[1], sync[2]); DEBUG4("P25RX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
DEBUG4("P25RX::correlateSync() sync [b3 - b5]", sync[3], sync[4], sync[5]); DEBUG4("P25RX: correlateSync(): sync [b3 - b5]", sync[3], sync[4], sync[5]);
for (uint8_t i = 0U; i < P25_SYNC_BYTES_LENGTH; i++) for (uint8_t i = 0U; i < P25_SYNC_BYTES_LENGTH; i++)
m_buffer[i] = sync[i]; m_buffer[i] = sync[i];
@ -406,14 +424,11 @@ bool P25RX::correlateSync()
m_endPtr = m_dataPtr + P25_LDU_FRAME_LENGTH_BITS - P25_SYNC_LENGTH_BITS; m_endPtr = m_dataPtr + P25_LDU_FRAME_LENGTH_BITS - P25_SYNC_LENGTH_BITS;
if (m_endPtr >= P25_LDU_FRAME_LENGTH_BITS) if (m_endPtr >= P25_LDU_FRAME_LENGTH_BITS)
m_endPtr -= P25_LDU_FRAME_LENGTH_BITS; m_endPtr -= P25_LDU_FRAME_LENGTH_BITS;
m_pduEndPtr = m_dataPtr + P25_PDU_FRAME_LENGTH_BITS - P25_SYNC_LENGTH_BITS;
if (m_pduEndPtr >= P25_PDU_FRAME_LENGTH_BITS)
m_pduEndPtr -= P25_PDU_FRAME_LENGTH_BITS;
m_lostCount = MAX_SYNC_FRAMES; m_lostCount = MAX_SYNC_FRAMES;
m_dataPtr = P25_SYNC_LENGTH_BITS; m_dataPtr = P25_SYNC_LENGTH_BITS;
DEBUG4("P25RX::correlateSync() dataPtr/endPtr/pduEndPtr", m_dataPtr, m_endPtr, m_pduEndPtr); DEBUG3("P25RX: correlateSync(): dataPtr/endPtr", m_dataPtr, m_endPtr);
return true; return true;
} }
@ -421,8 +436,9 @@ bool P25RX::correlateSync()
return false; return false;
} }
/* Helper to decode the P25 NID. */ /// <summary>
/// Helper to decode the P25 NID.
/// </summary>
bool P25RX::decodeNid() bool P25RX::decodeNid()
{ {
uint8_t nid[P25_NID_LENGTH_BYTES]; uint8_t nid[P25_NID_LENGTH_BYTES];
@ -431,19 +447,21 @@ bool P25RX::decodeNid()
if (m_nac == 0xF7EU) { if (m_nac == 0xF7EU) {
m_duid = nid[1U] & 0x0FU; m_duid = nid[1U] & 0x0FU;
DEBUG2("P25RX::decodeNid() DUID for xDU", m_duid); DEBUG2("P25RX: decodeNid(): DUID for xDU", m_duid);
return true; return true;
} }
uint16_t nac = (nid[0U] << 4) | ((nid[1U] & 0xF0U) >> 4); uint16_t nac = (nid[0U] << 4) | ((nid[1U] & 0xF0U) >> 4);
if (nac == m_nac) { if (nac == m_nac) {
m_duid = nid[1U] & 0x0FU; m_duid = nid[1U] & 0x0FU;
DEBUG2("P25RX::decodeNid() DUID for xDU", m_duid); DEBUG2("P25RX: decodeNid(): DUID for xDU", m_duid);
return true; return true;
} }
else { else {
DEBUG3("P25RX::decodeNid() invalid NAC found; nac != m_nac", nac, m_nac); DEBUG3("P25RX: decodeNid(): invalid NAC found; nac != m_nac", nac, m_nac);
} }
return false; return false;
} }
#endif // defined(ENABLE_P25)

@ -1,83 +1,81 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017,2018 Andy Uribe, CA6JAU
* Copyright (C) 2021-2024 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file P25RX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup p25_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file P25RX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup p25_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
* Copyright (C) 2021 Bryan Biedenkapp N2PLL
*
* 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(__P25_RX_H__) #if !defined(__P25_RX_H__)
#define __P25_RX_H__ #define __P25_RX_H__
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#if defined(ENABLE_P25)
namespace p25 namespace p25
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Constants // Constants
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief P25 Receiver State
* @ingroup p25_hfw
*/
enum P25RX_STATE { enum P25RX_STATE {
P25RXS_NONE, //! None P25RXS_NONE,
P25RXS_SYNC, //! Found Sync P25RXS_SYNC,
P25RXS_VOICE, //! Voice Data P25RXS_VOICE,
P25RXS_DATA //! PDU Data P25RXS_DATA
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements receiver logic for P25 mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements receiver logic for P25 mode operation.
* @ingroup p25_hfw
*/
class DSP_FW_API P25RX { class DSP_FW_API P25RX {
public: public:
/** /// <summary>Initializes a new instance of the P25RX class.</summary>
* @brief Initializes a new instance of the P25RX class.
*/
P25RX(); P25RX();
/** /// <summary>Helper to reset data values to defaults.</summary>
* @brief Helper to reset data values to defaults.
*/
void reset(); void reset();
/** /// <summary>Sample P25 bits from the air interface.</summary>
* @brief Sample P25 bits from the air interface.
* @param bit
*/
void databit(bool bit); void databit(bool bit);
/** /// <summary>Sets the P25 NAC.</summary>
* @brief Sets the P25 NAC.
* @param nac Network Access Code.
*/
void setNAC(uint16_t nac); void setNAC(uint16_t nac);
private: private:
uint64_t m_bitBuffer; uint64_t m_bitBuffer;
uint8_t m_buffer[P25_PDU_FRAME_LENGTH_BYTES + 3U]; uint8_t m_buffer[P25_LDU_FRAME_LENGTH_BYTES + 3U];
uint16_t m_dataPtr; uint16_t m_dataPtr;
uint16_t m_endPtr; uint16_t m_endPtr;
uint16_t m_pduEndPtr;
uint16_t m_lostCount; uint16_t m_lostCount;
@ -87,34 +85,21 @@ namespace p25
uint8_t m_duid; uint8_t m_duid;
/** /// <summary>Helper to process P25 bits.</summary>
* @brief Helper to process P25 bits.
* @param bit
*/
void processBit(bool bit); void processBit(bool bit);
/** /// <summary>Helper to process LDU P25 bits.</summary>
* @brief Helper to process LDU P25 bits.
* @param bit
*/
void processVoice(bool bit); void processVoice(bool bit);
/** /// <summary>Helper to process PDU P25 bits.</summary>
* @brief Helper to process PDU P25 bits.
* @param bit
*/
void processData(bool bit); void processData(bool bit);
/** /// <summary>Frame synchronization correlator.</summary>
* @brief Frame synchronization correlator.
* @returns bool
*/
bool correlateSync(); bool correlateSync();
/** /// <summary>Helper to decode the P25 NID.</summary>
* @brief Helper to decode the P25 NID.
* @returns bool True, if P25 NID was decoded, otherwise false.
*/
bool decodeNid(); bool decodeNid();
}; };
} // namespace p25 } // namespace p25
#endif // defined(ENABLE_P25)
#endif // __P25_RX_H__ #endif // __P25_RX_H__

@ -1,26 +1,49 @@
// SPDX-License-Identifier: GPL-2.0-only /**
* Digital Voice Modem - DSP Firmware (Hotspot)
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/* /*
* Digital Voice Modem - Hotspot Firmware * Copyright (C) 2016 by Jonathan Naylor G4KLX
* GPLv2 Open Source. Use is subject to license terms. * Copyright (C) 2016,2017 by Andy Uribe CA6JAU
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * Copyright (C) 2021-2022 by Bryan Biedenkapp N2PLL
* *
* Copyright (C) 2016 Jonathan Naylor, G4KLX * This program is free software; you can redistribute it and/or modify
* Copyright (C) 2016,2017 Andy Uribe, CA6JAU * it under the terms of the GNU General Public License as published by
* Copyright (C) 2021-2022 Bryan Biedenkapp, N2PLL * 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 "Globals.h" #include "Globals.h"
#include "p25/P25TX.h" #include "p25/P25TX.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
using namespace p25; using namespace p25;
#if defined(ENABLE_P25)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Public Class Members // Public Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Initializes a new instance of the P25TX class. */ /// <summary>
/// Initializes a new instance of the P25TX class.
/// </summary>
P25TX::P25TX() : P25TX::P25TX() :
m_fifo(P25_TX_BUFFER_LEN), m_fifo(P25_TX_BUFFER_LEN),
m_state(P25TXSTATE_NORMAL), m_state(P25TXSTATE_NORMAL),
@ -34,8 +57,9 @@ P25TX::P25TX() :
/* stub */ /* stub */
} }
/* Process local buffer and transmit on the air interface. */ /// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void P25TX::process() void P25TX::process()
{ {
if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U && if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U &&
@ -72,6 +96,8 @@ void P25TX::process()
createData(); createData();
} }
DEBUG2("P25TX: process(): poLen", m_poLen);
} }
if (m_poLen > 0U) { if (m_poLen > 0U) {
@ -94,44 +120,43 @@ void P25TX::process()
} }
} }
/* Write data to the local buffer. */ /// <summary>
/// Write data to the local buffer.
uint8_t P25TX::writeData(const uint8_t* data, uint16_t length) /// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t P25TX::writeData(const uint8_t* data, uint8_t length)
{ {
if (length < (P25_TDU_FRAME_LENGTH_BYTES + 1U)) if (length < (P25_TDU_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace(); uint16_t space = m_fifo.getSpace();
DEBUG3("P25TX::writeData() dataLength/fifoLength", length, space); DEBUG3("P25TX: writeData(): dataLength/fifoLength", length, space);
if (space < length) { if (space < length) {
m_fifo.reset(); m_fifo.reset();
return RSN_RINGBUFF_FULL; return RSN_RINGBUFF_FULL;
} }
if (length <= 255U) {
m_fifo.put(DVM_SHORT_FRAME_START);
m_fifo.put(length - 1U); m_fifo.put(length - 1U);
} else { for (uint8_t i = 0U; i < (length - 1U); i++)
m_fifo.put(DVM_LONG_FRAME_START);
m_fifo.put(((length - 1U) >> 8U) & 0xFFU);
m_fifo.put((length - 1U) & 0xFFU);
}
for (uint16_t i = 0U; i < (length - 1U); i++)
m_fifo.put(data[i + 1U]); m_fifo.put(data[i + 1U]);
return RSN_OK; return RSN_OK;
} }
/* Clears the local buffer. */ /// <summary>
/// Clears the local buffer.
/// </summary>
void P25TX::clear() void P25TX::clear()
{ {
m_fifo.reset(); m_fifo.reset();
} }
/* Sets the FDMA preamble count. */ /// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void P25TX::setPreambleCount(uint8_t preambleCnt) void P25TX::setPreambleCount(uint8_t preambleCnt)
{ {
m_preambleCnt = P25_FIXED_DELAY + preambleCnt; m_preambleCnt = P25_FIXED_DELAY + preambleCnt;
@ -141,8 +166,10 @@ void P25TX::setPreambleCount(uint8_t preambleCnt)
m_preambleCnt = 1200U; m_preambleCnt = 1200U;
} }
/* Sets the Tx hang time. */ /// <summary>
/// Sets the Tx hang time.
/// </summary>
/// <param name="txHang">Transmit hang time in seconds.</param>
void P25TX::setTxHang(uint8_t txHang) void P25TX::setTxHang(uint8_t txHang)
{ {
if (txHang > 0U) if (txHang > 0U)
@ -155,23 +182,19 @@ void P25TX::setTxHang(uint8_t txHang)
m_txHang = 13U * 1200U; m_txHang = 13U * 1200U;
} }
/* Helper to set the calibration state for Tx. */ /// <summary>
/// Helper to set the calibration state for Tx.
/// </summary>
/// <param name="start"></param>
void P25TX::setCal(bool start) void P25TX::setCal(bool start)
{ {
m_state = start ? P25TXSTATE_CAL : P25TXSTATE_NORMAL; m_state = start ? P25TXSTATE_CAL : P25TXSTATE_NORMAL;
} }
/* Helper to resize the FIFO buffer. */ /// <summary>
/// Helper to get how much space the ring buffer has for samples.
void P25TX::resizeBuffer(uint16_t size) /// </summary>
{ /// <returns></returns>
m_fifo.reset();
m_fifo.reinitialize(size);
}
/* Helper to get how much space the ring buffer has for samples. */
uint8_t P25TX::getSpace() const uint8_t P25TX::getSpace() const
{ {
return m_fifo.getSpace() / P25_LDU_FRAME_LENGTH_BYTES; return m_fifo.getSpace() / P25_LDU_FRAME_LENGTH_BYTES;
@ -181,8 +204,9 @@ uint8_t P25TX::getSpace() const
// Private Class Members // Private Class Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/* Helper to generate data. */ /// <summary>
///
/// </summary>
void P25TX::createData() void P25TX::createData()
{ {
if (!m_tx) { if (!m_tx) {
@ -190,19 +214,9 @@ void P25TX::createData()
m_poBuffer[m_poLen++] = P25_START_SYNC; m_poBuffer[m_poLen++] = P25_START_SYNC;
} }
else { else {
uint8_t frameType = m_fifo.get(); uint8_t length = m_fifo.get();
uint16_t length = 0U; DEBUG3("P25TX: createData(): dataLength/fifoSpace", length, m_fifo.getSpace());
switch (frameType) { for (uint8_t i = 0U; i < length; i++) {
case DVM_SHORT_FRAME_START:
length = m_fifo.get();
break;
case DVM_LONG_FRAME_START:
length = ((m_fifo.get() & 0xFFU) << 8) + (m_fifo.get());
break;
}
DEBUG3("P25TX::createData() dataLength/fifoSpace", length, m_fifo.getSpace());
for (uint16_t i = 0U; i < length; i++) {
m_poBuffer[m_poLen++] = m_fifo.get(); m_poBuffer[m_poLen++] = m_fifo.get();
} }
} }
@ -210,13 +224,14 @@ void P25TX::createData()
m_poPtr = 0U; m_poPtr = 0U;
} }
/* Helper to generate calibration data. */ /// <summary>
///
/// </summary>
void P25TX::createCal() void P25TX::createCal()
{ {
// 1.2 kHz sine wave generation // 1.2 kHz sine wave generation
if (m_modemState == STATE_P25_CAL) { if (m_modemState == STATE_P25_CAL) {
for (uint8_t i = 0U; i < P25_LDU_FRAME_LENGTH_BYTES; i++) { for (unsigned int i = 0U; i < P25_LDU_FRAME_LENGTH_BYTES; i++) {
m_poBuffer[i] = P25_START_SYNC; m_poBuffer[i] = P25_START_SYNC;
} }
@ -227,8 +242,10 @@ void P25TX::createCal()
m_poPtr = 0U; m_poPtr = 0U;
} }
/* Helper to write a raw byte to the DAC. */ /// <summary>
///
/// </summary>
/// <param name="c"></param>
void P25TX::writeByte(uint8_t c) void P25TX::writeByte(uint8_t c)
{ {
uint8_t bit; uint8_t bit;
@ -244,8 +261,9 @@ void P25TX::writeByte(uint8_t c)
} }
} }
/* */ /// <summary>
///
/// </summary>
void P25TX::writeSilence() void P25TX::writeSilence()
{ {
uint8_t bit; uint8_t bit;
@ -254,3 +272,5 @@ void P25TX::writeSilence()
io.write(&bit, 1); io.write(&bit, 1);
} }
} }
#endif // defined(ENABLE_P25)

@ -1,26 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Digital Voice Modem - Hotspot Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX
* Copyright (C) 2016,2017 Andy Uribe, CA6JAU
* Copyright (C) 2021-2022 Bryan Biedenkapp, N2PLL
*
*/
/** /**
* @file P25TX.h * Digital Voice Modem - DSP Firmware (Hotspot)
* @ingroup p25_hfw * GPLv2 Open Source. Use is subject to license terms.
* @file P25TX.cpp * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* @ingroup p25_hfw *
*/ * @package DVM / DSP Firmware (Hotspot)
*
*/
//
// Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017 by Andy Uribe CA6JAU
* Copyright (C) 2021-2022 by Bryan Biedenkapp N2PLL
*
* 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(__P25_TX_H__) #if !defined(__P25_TX_H__)
#define __P25_TX_H__ #define __P25_TX_H__
#include "Defines.h" #include "Defines.h"
#include "SerialBuffer.h" #include "SerialBuffer.h"
#if defined(ENABLE_P25)
namespace p25 namespace p25
{ {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -30,74 +46,38 @@ namespace p25
#define P25_FIXED_DELAY 90 // 90 = 20ms #define P25_FIXED_DELAY 90 // 90 = 20ms
#define P25_FIXED_TX_HANG 750 // 750 = 625ms #define P25_FIXED_TX_HANG 750 // 750 = 625ms
/**
* @brief P25 Transmitter State
* @ingroup p25_hfw
*/
enum P25TXSTATE { enum P25TXSTATE {
P25TXSTATE_NORMAL, //! Normal P25TXSTATE_NORMAL,
P25TXSTATE_CAL //! Calibration P25TXSTATE_CAL
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
// Implements transmitter logic for P25 mode operation.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/**
* @brief Implements transmitter logic for P25 mode operation.
* @ingroup p25_hfw
*/
class DSP_FW_API P25TX { class DSP_FW_API P25TX {
public: public:
/** /// <summary>Initializes a new instance of the P25TX class.</summary>
* @brief Initializes a new instance of the P25TX class.
*/
P25TX(); P25TX();
/** /// <summary>Process local buffer and transmit on the air interface.</summary>
* @brief Process local buffer and transmit on the air interface.
*/
void process(); void process();
/** /// <summary>Write data to the local buffer.</summary>
* @brief Write data to the local buffer. uint8_t writeData(const uint8_t* data, uint8_t length);
* @param[in] data Buffer.
* @param length Length of buffer. /// <summary>Clears the local buffer.</summary>
* @returns uint8_t Reason code.
*/
uint8_t writeData(const uint8_t* data, uint16_t length);
/**
* @brief Clears the local buffer.
*/
void clear(); void clear();
/** /// <summary>Sets the FDMA preamble count.</summary>
* @brief Sets the FDMA preamble count.
* @param preambleCnt FDMA preamble count.
*/
void setPreambleCount(uint8_t preambleCnt); void setPreambleCount(uint8_t preambleCnt);
/** /// <summary>Sets the transmit hang time.</summary>
* @brief Sets the transmit hang time.
* @param txHang Transmit hang time.
*/
void setTxHang(uint8_t txHang); void setTxHang(uint8_t txHang);
/** /// <summary>Helper to set the calibration state for Tx.</summary>
* @brief Helper to set the calibration state for Tx.
* @param start
*/
void setCal(bool start); void setCal(bool start);
/** /// <summary>Helper to get how much space the ring buffer has for samples.</summary>
* @brief Helper to resize the FIFO buffer.
* @param size
*/
void resizeBuffer(uint16_t size);
/**
* @brief Helper to get how much space the ring buffer has for samples.
* @returns uint8_t Amount of space in ring buffer for samples.
*/
uint8_t getSpace() const; uint8_t getSpace() const;
private: private:
@ -113,25 +93,18 @@ namespace p25
uint32_t m_txHang; uint32_t m_txHang;
uint32_t m_tailCnt; uint32_t m_tailCnt;
/** /// <summary></summary>
* @brief Helper to generate data.
*/
void createData(); void createData();
/** /// <summary></summary>
* @brief Helper to generate calibration data.
*/
void createCal(); void createCal();
/** /// <summary></summary>
* @brief Helper to write a raw byte to the DAC.
* @param c Byte.
*/
void writeByte(uint8_t c); void writeByte(uint8_t c);
/** /// <summary></summary>
* @brief
*/
void writeSilence(); void writeSilence();
}; };
} // namespace p25 } // namespace p25
#endif // defined(ENABLE_P25)
#endif // __P25_TX_H__ #endif // __P25_TX_H__

@ -0,0 +1,26 @@
/*
* Copyright (C) 2016-2018 by Andy Uribe CA6JAU
*
* 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.
*/
/* Memory areas */
MEMORY
{
ROM (rx) : ORIGIN = 0x08002000, LENGTH = 120K /* FLASH */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K /* Main RAM */
}
INCLUDE stm32f10x_link.ld
Loading…
Cancel
Save

Powered by TurnKey Linux.