diff --git a/Drivers/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_iwdg.h b/Drivers/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_iwdg.h
new file mode 100644
index 0000000..918f671
--- /dev/null
+++ b/Drivers/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_iwdg.h
@@ -0,0 +1,255 @@
+/**
+ ******************************************************************************
+ * @file stm32l4xx_hal_iwdg.h
+ * @author MCD Application Team
+ * @brief Header file of IWDG HAL module.
+ ******************************************************************************
+ * @attention
+ *
+ *
© COPYRIGHT(c) 2017 STMicroelectronics
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32L4xx_HAL_IWDG_H
+#define __STM32L4xx_HAL_IWDG_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx_hal_def.h"
+
+/** @addtogroup STM32L4xx_HAL_Driver
+ * @{
+ */
+
+/** @defgroup IWDG IWDG
+ * @{
+ */
+
+/* Exported types ------------------------------------------------------------*/
+/** @defgroup IWDG_Exported_Types IWDG Exported Types
+ * @{
+ */
+
+/**
+ * @brief IWDG Init structure definition
+ */
+typedef struct
+{
+ uint32_t Prescaler; /*!< Select the prescaler of the IWDG.
+ This parameter can be a value of @ref IWDG_Prescaler */
+
+ uint32_t Reload; /*!< Specifies the IWDG down-counter reload value.
+ This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */
+
+ uint32_t Window; /*!< Specifies the window value to be compared to the down-counter.
+ This parameter must be a number between Min_Data = 0 and Max_Data = 0x0FFF */
+
+} IWDG_InitTypeDef;
+
+/**
+ * @brief IWDG Handle Structure definition
+ */
+typedef struct
+{
+ IWDG_TypeDef *Instance; /*!< Register base address */
+
+ IWDG_InitTypeDef Init; /*!< IWDG required parameters */
+
+}IWDG_HandleTypeDef;
+
+/**
+ * @}
+ */
+
+/* Exported constants --------------------------------------------------------*/
+/** @defgroup IWDG_Exported_Constants IWDG Exported Constants
+ * @{
+ */
+
+/** @defgroup IWDG_Prescaler IWDG Prescaler
+ * @{
+ */
+#define IWDG_PRESCALER_4 0x00000000u /*!< IWDG prescaler set to 4 */
+#define IWDG_PRESCALER_8 IWDG_PR_PR_0 /*!< IWDG prescaler set to 8 */
+#define IWDG_PRESCALER_16 IWDG_PR_PR_1 /*!< IWDG prescaler set to 16 */
+#define IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 32 */
+#define IWDG_PRESCALER_64 IWDG_PR_PR_2 /*!< IWDG prescaler set to 64 */
+#define IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< IWDG prescaler set to 128 */
+#define IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< IWDG prescaler set to 256 */
+/**
+ * @}
+ */
+
+/** @defgroup IWDG_Window_option IWDG Window option
+ * @{
+ */
+#define IWDG_WINDOW_DISABLE IWDG_WINR_WIN
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/* Exported macros -----------------------------------------------------------*/
+/** @defgroup IWDG_Exported_Macros IWDG Exported Macros
+ * @{
+ */
+
+/**
+ * @brief Enable the IWDG peripheral.
+ * @param __HANDLE__ IWDG handle
+ * @retval None
+ */
+#define __HAL_IWDG_START(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_ENABLE)
+
+/**
+ * @brief Reload IWDG counter with value defined in the reload register
+ * (write access to IWDG_PR, IWDG_RLR & IWDG_WINR registers disabled).
+ * @param __HANDLE__ IWDG handle
+ * @retval None
+ */
+#define __HAL_IWDG_RELOAD_COUNTER(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_RELOAD)
+
+/**
+ * @}
+ */
+
+/* Exported functions --------------------------------------------------------*/
+/** @defgroup IWDG_Exported_Functions IWDG Exported Functions
+ * @{
+ */
+
+/** @defgroup IWDG_Exported_Functions_Group1 Initialization and Start functions
+ * @{
+ */
+/* Initialization/Start functions ********************************************/
+HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg);
+/**
+ * @}
+ */
+
+/** @defgroup IWDG_Exported_Functions_Group2 IO operation functions
+ * @{
+ */
+/* I/O operation functions ****************************************************/
+HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg);
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/* Private constants ---------------------------------------------------------*/
+/** @defgroup IWDG_Private_Constants IWDG Private Constants
+ * @{
+ */
+
+/**
+ * @brief IWDG Key Register BitMask
+ */
+#define IWDG_KEY_RELOAD 0x0000AAAAu /*!< IWDG Reload Counter Enable */
+#define IWDG_KEY_ENABLE 0x0000CCCCu /*!< IWDG Peripheral Enable */
+#define IWDG_KEY_WRITE_ACCESS_ENABLE 0x00005555u /*!< IWDG KR Write Access Enable */
+#define IWDG_KEY_WRITE_ACCESS_DISABLE 0x00000000u /*!< IWDG KR Write Access Disable */
+
+/**
+ * @}
+ */
+
+/* Private macros ------------------------------------------------------------*/
+/** @defgroup IWDG_Private_Macros IWDG Private Macros
+ * @{
+ */
+
+/**
+ * @brief Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers.
+ * @param __HANDLE__ IWDG handle
+ * @retval None
+ */
+#define IWDG_ENABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_ENABLE)
+
+/**
+ * @brief Disable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers.
+ * @param __HANDLE__ IWDG handle
+ * @retval None
+ */
+#define IWDG_DISABLE_WRITE_ACCESS(__HANDLE__) WRITE_REG((__HANDLE__)->Instance->KR, IWDG_KEY_WRITE_ACCESS_DISABLE)
+
+/**
+ * @brief Check IWDG prescaler value.
+ * @param __PRESCALER__ IWDG prescaler value
+ * @retval None
+ */
+#define IS_IWDG_PRESCALER(__PRESCALER__) (((__PRESCALER__) == IWDG_PRESCALER_4) || \
+ ((__PRESCALER__) == IWDG_PRESCALER_8) || \
+ ((__PRESCALER__) == IWDG_PRESCALER_16) || \
+ ((__PRESCALER__) == IWDG_PRESCALER_32) || \
+ ((__PRESCALER__) == IWDG_PRESCALER_64) || \
+ ((__PRESCALER__) == IWDG_PRESCALER_128)|| \
+ ((__PRESCALER__) == IWDG_PRESCALER_256))
+
+/**
+ * @brief Check IWDG reload value.
+ * @param __RELOAD__ IWDG reload value
+ * @retval None
+ */
+#define IS_IWDG_RELOAD(__RELOAD__) ((__RELOAD__) <= IWDG_RLR_RL)
+
+/**
+ * @brief Check IWDG window value.
+ * @param __WINDOW__ IWDG window value
+ * @retval None
+ */
+#define IS_IWDG_WINDOW(__WINDOW__) ((__WINDOW__) <= IWDG_WINR_WIN)
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32L4xx_HAL_IWDG_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_iwdg.c b/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_iwdg.c
new file mode 100644
index 0000000..2cf5b4a
--- /dev/null
+++ b/Drivers/STM32L4xx_HAL_Driver/Src/stm32l4xx_hal_iwdg.c
@@ -0,0 +1,280 @@
+/**
+ ******************************************************************************
+ * @file stm32l4xx_hal_iwdg.c
+ * @author MCD Application Team
+ * @brief IWDG HAL module driver.
+ * This file provides firmware functions to manage the following
+ * functionalities of the Independent Watchdog (IWDG) peripheral:
+ * + Initialization and Start functions
+ * + IO operation functions
+ *
+ @verbatim
+ ==============================================================================
+ ##### IWDG Generic features #####
+ ==============================================================================
+ [..]
+ (+) The IWDG can be started by either software or hardware (configurable
+ through option byte).
+
+ (+) The IWDG is clocked by Low-Speed clock (LSI) and thus stays active even
+ if the main clock fails.
+
+ (+) Once the IWDG is started, the LSI is forced ON and both can not be
+ disabled. The counter starts counting down from the reset value (0xFFF).
+ When it reaches the end of count value (0x000) a reset signal is
+ generated (IWDG reset).
+
+ (+) Whenever the key value 0x0000 AAAA is written in the IWDG_KR register,
+ the IWDG_RLR value is reloaded in the counter and the watchdog reset is
+ prevented.
+
+ (+) The IWDG is implemented in the VDD voltage domain that is still functional
+ in STOP and STANDBY mode (IWDG reset can wake-up from STANDBY).
+ IWDGRST flag in RCC_CSR register can be used to inform when an IWDG
+ reset occurs.
+
+ (+) Debug mode : When the microcontroller enters debug mode (core halted),
+ the IWDG counter either continues to work normally or stops, depending
+ on DBG_IWDG_STOP configuration bit in DBG module, accessible through
+ __HAL_DBGMCU_FREEZE_IWDG() and __HAL_DBGMCU_UNFREEZE_IWDG() macros
+
+ [..] Min-max timeout value @32KHz (LSI): ~125us / ~32.7s
+ The IWDG timeout may vary due to LSI frequency dispersion. STM32L4xx
+ devices provide the capability to measure the LSI frequency (LSI clock
+ connected internally to TIM16 CH1 input capture). The measured value
+ can be used to have an IWDG timeout with an acceptable accuracy.
+
+ ##### How to use this driver #####
+ ==============================================================================
+ [..]
+ (#) Use IWDG using HAL_IWDG_Init() function to :
+ (++) Enable instance by writing Start keyword in IWDG_KEY register. LSI
+ clock is forced ON and IWDG counter starts downcounting.
+ (++) Enable write access to configuration register: IWDG_PR, IWDG_RLR &
+ IWDG_WINR.
+ (++) Configure the IWDG prescaler and counter reload value. This reload
+ value will be loaded in the IWDG counter each time the watchdog is
+ reloaded, then the IWDG will start counting down from this value.
+ (++) Wait for status flags to be reset
+ (++) Depending on window parameter:
+ (+++) If Window Init parameter is same as Window register value,
+ nothing more is done but reload counter value in order to exit
+ function withy exact time base.
+ (+++) Else modify Window register. This will automatically reload
+ watchdog counter.
+
+ (#) Then the application program must refresh the IWDG counter at regular
+ intervals during normal operation to prevent an MCU reset, using
+ HAL_IWDG_Refresh() function.
+
+ *** IWDG HAL driver macros list ***
+ ====================================
+ [..]
+ Below the list of most used macros in IWDG HAL driver:
+ (+) __HAL_IWDG_START: Enable the IWDG peripheral
+ (+) __HAL_IWDG_RELOAD_COUNTER: Reloads IWDG counter with value defined in
+ the reload register
+
+ @endverbatim
+ ******************************************************************************
+ * @attention
+ *
+ * © COPYRIGHT(c) 2017 STMicroelectronics
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx_hal.h"
+
+/** @addtogroup STM32L4xx_HAL_Driver
+ * @{
+ */
+
+#ifdef HAL_IWDG_MODULE_ENABLED
+/** @addtogroup IWDG
+ * @brief IWDG HAL module driver.
+ * @{
+ */
+
+/* Private typedef -----------------------------------------------------------*/
+/* Private define ------------------------------------------------------------*/
+/** @defgroup IWDG_Private_Defines IWDG Private Defines
+ * @{
+ */
+/* Status register need 5 RC LSI divided by prescaler clock to be updated. With
+ higher prescaler (256), and according to HSI variation, we need to wait at
+ least 6 cycles so 48 ms. */
+#define HAL_IWDG_DEFAULT_TIMEOUT 48u
+/**
+ * @}
+ */
+
+/* Private macro -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private function prototypes -----------------------------------------------*/
+/* Exported functions --------------------------------------------------------*/
+
+/** @addtogroup IWDG_Exported_Functions
+ * @{
+ */
+
+/** @addtogroup IWDG_Exported_Functions_Group1
+ * @brief Initialization and Start functions.
+ *
+@verbatim
+ ===============================================================================
+ ##### Initialization and Start functions #####
+ ===============================================================================
+ [..] This section provides functions allowing to:
+ (+) Initialize the IWDG according to the specified parameters in the
+ IWDG_InitTypeDef of associated handle.
+ (+) Manage Window option.
+ (+) Once initialization is performed in HAL_IWDG_Init function, Watchdog
+ is reloaded in order to exit function with correct time base.
+
+@endverbatim
+ * @{
+ */
+
+/**
+ * @brief Initialize the IWDG according to the specified parameters in the
+ * IWDG_InitTypeDef and start watchdog. Before exiting function,
+ * watchdog is refreshed in order to have correct time base.
+ * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
+ * the configuration information for the specified IWDG module.
+ * @retval HAL status
+ */
+HAL_StatusTypeDef HAL_IWDG_Init(IWDG_HandleTypeDef *hiwdg)
+{
+ uint32_t tickstart;
+
+ /* Check the IWDG handle allocation */
+ if(hiwdg == NULL)
+ {
+ return HAL_ERROR;
+ }
+
+ /* Check the parameters */
+ assert_param(IS_IWDG_ALL_INSTANCE(hiwdg->Instance));
+ assert_param(IS_IWDG_PRESCALER(hiwdg->Init.Prescaler));
+ assert_param(IS_IWDG_RELOAD(hiwdg->Init.Reload));
+ assert_param(IS_IWDG_WINDOW(hiwdg->Init.Window));
+
+ /* Enable IWDG. LSI is turned on automaticaly */
+ __HAL_IWDG_START(hiwdg);
+
+ /* Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers by writing
+ 0x5555 in KR */
+ IWDG_ENABLE_WRITE_ACCESS(hiwdg);
+
+ /* Write to IWDG registers the Prescaler & Reload values to work with */
+ hiwdg->Instance->PR = hiwdg->Init.Prescaler;
+ hiwdg->Instance->RLR = hiwdg->Init.Reload;
+
+ /* Check pending flag, if previous update not done, return timeout */
+ tickstart = HAL_GetTick();
+
+ /* Wait for register to be updated */
+ while (hiwdg->Instance->SR != 0x00u)
+ {
+ if((HAL_GetTick() - tickstart ) > HAL_IWDG_DEFAULT_TIMEOUT)
+ {
+ return HAL_TIMEOUT;
+ }
+ }
+
+ /* If window parameter is different than current value, modify window
+ register */
+ if(hiwdg->Instance->WINR != hiwdg->Init.Window)
+ {
+ /* Write to IWDG WINR the IWDG_Window value to compare with. In any case,
+ even if window feature is disabled, Watchdog will be reloaded by writing
+ windows register */
+ hiwdg->Instance->WINR = hiwdg->Init.Window;
+ }
+ else
+ {
+ /* Reload IWDG counter with value defined in the reload register */
+ __HAL_IWDG_RELOAD_COUNTER(hiwdg);
+ }
+
+ /* Return function status */
+ return HAL_OK;
+}
+
+/**
+ * @}
+ */
+
+
+/** @addtogroup IWDG_Exported_Functions_Group2
+ * @brief IO operation functions
+ *
+@verbatim
+ ===============================================================================
+ ##### IO operation functions #####
+ ===============================================================================
+ [..] This section provides functions allowing to:
+ (+) Refresh the IWDG.
+
+@endverbatim
+ * @{
+ */
+
+
+/**
+ * @brief Refresh the IWDG.
+ * @param hiwdg pointer to a IWDG_HandleTypeDef structure that contains
+ * the configuration information for the specified IWDG module.
+ * @retval HAL status
+ */
+HAL_StatusTypeDef HAL_IWDG_Refresh(IWDG_HandleTypeDef *hiwdg)
+{
+ /* Reload IWDG counter with value defined in the reload register */
+ __HAL_IWDG_RELOAD_COUNTER(hiwdg);
+
+ /* Return function status */
+ return HAL_OK;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* HAL_IWDG_MODULE_ENABLED */
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/Inc/main.h b/Inc/main.h
index 6391ab2..50dbb8d 100644
--- a/Inc/main.h
+++ b/Inc/main.h
@@ -101,6 +101,27 @@
/* USER CODE BEGIN Private defines */
+// #define TNC_HAS_LSCO -- Not available on NucleoTNC
+// #define TNC_HAS_SWO -- Not available on NucleoTNC
+#define TNC_HAS_LSE
+// #define TNC_HAS_HSE -- Not available on NucleoTNC
+// #define TNC_HAS_MCO -- Not available on NucleoTNC
+// #define TNC_HAS_BT -- Not available on NucleoTNC
+// #define TNC_HAS_BAT -- Not available on NucleoTNC
+// #define TNC_HAS_USB -- Not available on NucleoTNC
+
+// Compatibility defines
+// #define BATTERY_ADC_HANDLE hadc2
+// #define BATTERY_ADC_CHANNEL ADC_CHANNEL_16
+#define LED_PWM_TIMER_HANDLE htim1
+#define EEPROM_I2C hi2c3
+#define SERIAL_UART huart2
+#define AUDIO_ADC_HANDLE hadc1
+#define AUDIO_ADC_CHANNEL CHANNEL_8
+
+//#define USB_CE_Pin GPIO_PIN_4
+//#define USB_CE_GPIO_Port GPIOB
+
#define EEPROM_ADDRESS 0xA0
#define EEPROM_CAPACITY 4096
#define EEPROM_PAGE_SIZE 32
diff --git a/Inc/stm32l4xx_hal_conf.h b/Inc/stm32l4xx_hal_conf.h
index 31b37cc..079bd4c 100644
--- a/Inc/stm32l4xx_hal_conf.h
+++ b/Inc/stm32l4xx_hal_conf.h
@@ -67,7 +67,7 @@
/*#define HAL_HASH_MODULE_ENABLED */
/*#define HAL_I2S_MODULE_ENABLED */
/*#define HAL_IRDA_MODULE_ENABLED */
-/*#define HAL_IWDG_MODULE_ENABLED */
+#define HAL_IWDG_MODULE_ENABLED
/*#define HAL_LTDC_MODULE_ENABLED */
/*#define HAL_LCD_MODULE_ENABLED */
/*#define HAL_LPTIM_MODULE_ENABLED */
diff --git a/Src/main.c b/Src/main.c
index 12591aa..70815fa 100644
--- a/Src/main.c
+++ b/Src/main.c
@@ -72,6 +72,8 @@ DMA_HandleTypeDef hdma_i2c3_tx;
OPAMP_HandleTypeDef hopamp1;
+IWDG_HandleTypeDef hiwdg;
+
RNG_HandleTypeDef hrng;
RTC_HandleTypeDef hrtc;
@@ -145,6 +147,7 @@ static void MX_USART2_UART_Init(void);
static void MX_I2C3_Init(void);
static void MX_TIM1_Init(void);
static void MX_RNG_Init(void);
+static void MX_IWDG_Init(void);
void startDefaultTask(void const * argument);
extern void startIOEventTask(void const * argument);
extern void startAudioInputTask(void const * argument);
@@ -636,6 +639,21 @@ static void MX_I2C3_Init(void)
}
+/* IWDG init function */
+static void MX_IWDG_Init(void)
+{
+
+ hiwdg.Instance = IWDG;
+ hiwdg.Init.Prescaler = IWDG_PRESCALER_16;
+ hiwdg.Init.Window = 4095;
+ hiwdg.Init.Reload = 4095;
+ if (HAL_IWDG_Init(&hiwdg) != HAL_OK)
+ {
+ _Error_Handler(__FILE__, __LINE__);
+ }
+
+}
+
/* OPAMP1 init function */
static void MX_OPAMP1_Init(void)
{
@@ -1239,7 +1257,7 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
}
/* USER CODE BEGIN Callback 1 */
if (htim->Instance == TIM1) {
- HTIM1_PeriodElapsedCallback();
+ LED_TIMER_PeriodElapsedCallback();
}
/* USER CODE END Callback 1 */
diff --git a/TNC/AFSKModulator.hpp b/TNC/AFSKModulator.hpp
index 6305885..db28302 100644
--- a/TNC/AFSKModulator.hpp
+++ b/TNC/AFSKModulator.hpp
@@ -19,6 +19,7 @@ extern osMessageQId hdlcOutputQueueHandle;
extern osMessageQId dacOutputQueueHandle;
extern TIM_HandleTypeDef htim7;
extern DAC_HandleTypeDef hdac1;
+extern IWDG_HandleTypeDef hiwdg;
namespace mobilinkd { namespace tnc {
@@ -114,7 +115,7 @@ struct AFSKModulator : Modulator
switch (running_) {
case -1:
ptt_->on();
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_DisableLSCO();
#endif
@@ -132,8 +133,12 @@ struct AFSKModulator : Modulator
}
}
+ void tone(uint16_t freq) override {}
+
void fill(uint16_t* buffer, bool bit)
{
+ HAL_IWDG_Refresh(&hiwdg);
+
for (size_t i = 0; i != BIT_LEN; i++)
{
int s = sin_table[pos_];
@@ -152,7 +157,7 @@ struct AFSKModulator : Modulator
}
}
if (s < 0 or s > 4095) {
- DEBUG("DAC inversion (%d)", s);
+ TNC_DEBUG("DAC inversion (%d)", s);
}
*buffer = uint16_t(s);
++buffer;
@@ -183,6 +188,8 @@ struct AFSKModulator : Modulator
void empty()
{
+ HAL_IWDG_Refresh(&hiwdg);
+
switch (running_) {
case 1:
running_ = 0;
@@ -192,7 +199,7 @@ struct AFSKModulator : Modulator
stop_conversion();
ptt_->off();
pos_ = 0;
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
break;
@@ -207,7 +214,7 @@ struct AFSKModulator : Modulator
stop_conversion();
ptt_->off();
pos_ = 0;
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
diff --git a/TNC/AFSKTestTone.cpp b/TNC/AFSKTestTone.cpp
index 3c4a90b..2b2375c 100644
--- a/TNC/AFSKTestTone.cpp
+++ b/TNC/AFSKTestTone.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 Rob Riggs
+// Copyright 2016-2021 Rob Riggs
// All rights reserved.
#include "AFSKTestTone.hpp"
@@ -85,8 +85,7 @@ void AFSKTestTone::stop()
void AFSKTestTone::fill() const
{
static State current = State::SPACE;
- static uint32_t random = 0;
- static uint8_t counter = 0;
+
switch (state_) {
case AFSKTestTone::State::NONE:
@@ -104,6 +103,7 @@ void AFSKTestTone::fill() const
case AFSKTestTone::State::SPACE:
if (kiss::settings().modem_type == kiss::Hardware::ModemType::M17)
{
+ getModulator().tone(1);
getModulator().send(0x22);
}
else
@@ -114,17 +114,13 @@ void AFSKTestTone::fill() const
case AFSKTestTone::State::BOTH:
if (kiss::settings().modem_type == kiss::Hardware::ModemType::M17)
{
- if ((counter & 3) == 0)
- {
- auto status = HAL_RNG_GenerateRandomNumber(&hrng, &random);
- if (status != HAL_OK)
- {
- WARN("RNG failure code %d", status);
+ auto frame = getEncoder().create_bert_frame();
+ if (frame) {
+ for (auto c : *frame) {
+ getModulator().send(c);
}
+ hdlc::release(frame);
}
- getModulator().send(random & 0xFF);
- random >>= 8;
- counter += 1;
}
else
{
diff --git a/TNC/AFSKTestTone.hpp b/TNC/AFSKTestTone.hpp
index f73f971..ec3fbb4 100644
--- a/TNC/AFSKTestTone.hpp
+++ b/TNC/AFSKTestTone.hpp
@@ -1,8 +1,7 @@
-// Copyright 2016 Rob Riggs
+// Copyright 2016-2021 Rob Riggs
// All rights reserved.
-#ifndef MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
-#define MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
+#pragma once
#include "cmsis_os.h"
@@ -27,6 +26,3 @@ struct AFSKTestTone
};
}} // mobilinkd::tnc
-
-
-#endif // MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
diff --git a/TNC/Afsk1200Demodulator.cpp b/TNC/Afsk1200Demodulator.cpp
index 7c92336..b5028fe 100644
--- a/TNC/Afsk1200Demodulator.cpp
+++ b/TNC/Afsk1200Demodulator.cpp
@@ -85,7 +85,7 @@ hdlc::IoFrame* Afsk1200Demodulator::operator()(const q15_t* samples)
*/
float Afsk1200Demodulator::readTwist()
{
- DEBUG("enter Afsk1200Demodulator::readTwist");
+ TNC_DEBUG("enter Afsk1200Demodulator::readTwist");
float g1200 = 0.0f;
float g2200 = 0.0f;
@@ -133,14 +133,14 @@ float Afsk1200Demodulator::readTwist()
INFO("Twist = %d / 100 (%d - %d)", int(result * 100), int(g1200),
int(g2200));
- DEBUG("exit readTwist");
+ TNC_DEBUG("exit readTwist");
return result;
}
uint32_t Afsk1200Demodulator::readBatteryLevel()
{
#ifndef NUCLEOTNC
- DEBUG("enter Afsk1200Demodulator::readBatteryLevel");
+ TNC_DEBUG("enter Afsk1200Demodulator::readBatteryLevel");
ADC_ChannelConfTypeDef sConfig;
@@ -171,21 +171,21 @@ uint32_t Afsk1200Demodulator::readBatteryLevel()
gpio::BAT_DIVIDER::off();
HAL_Delay(1);
- sConfig.Channel = ADC_CHANNEL_15;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+ sConfig.Channel = BATTERY_ADC_CHANNEL;
+ if (HAL_ADC_ConfigChannel(&BATTERY_ADC_HANDLE, &sConfig) != HAL_OK)
CxxErrorHandler();
uint32_t vbat = 0;
- if (HAL_ADC_Start(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Start(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
for (size_t i = 0; i != 8; ++i)
{
- if (HAL_ADC_PollForConversion(&hadc1, 1) != HAL_OK) CxxErrorHandler();
- vbat += HAL_ADC_GetValue(&hadc1);
+ if (HAL_ADC_PollForConversion(&BATTERY_ADC_HANDLE, 1) != HAL_OK) CxxErrorHandler();
+ vbat += HAL_ADC_GetValue(&BATTERY_ADC_HANDLE);
}
vbat /= 8;
- if (HAL_ADC_Stop(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Stop(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
CxxErrorHandler();
@@ -206,7 +206,7 @@ uint32_t Afsk1200Demodulator::readBatteryLevel()
INFO("Vref = %lumV", vref);
INFO("Vbat = %lumV", vbat);
- DEBUG("exit Afsk1200Demodulator::readBatteryLevel");
+ TNC_DEBUG("exit Afsk1200Demodulator::readBatteryLevel");
return vbat;
#else
return 0;
diff --git a/TNC/AudioInput.cpp b/TNC/AudioInput.cpp
index c671b25..5a98b54 100644
--- a/TNC/AudioInput.cpp
+++ b/TNC/AudioInput.cpp
@@ -26,6 +26,7 @@
#include
extern osMessageQId ioEventQueueHandle;
+extern IWDG_HandleTypeDef hiwdg;
extern "C" void SystemClock_Config(void);
@@ -61,7 +62,7 @@ extern "C" void HAL_ADC_ErrorCallback(ADC_HandleTypeDef* /* hadc */) {
extern "C" void startAudioInputTask(void const*) {
using namespace mobilinkd::tnc::audio;
- DEBUG("startAudioInputTask");
+ TNC_DEBUG("startAudioInputTask");
adcPool.init();
@@ -74,7 +75,7 @@ extern "C" void startAudioInputTask(void const*) {
switch (adcState) {
case STOPPED:
- DEBUG("STOPPED");
+ TNC_DEBUG("STOPPED");
// stop();
break;
case DEMODULATOR:
@@ -82,41 +83,41 @@ extern "C" void startAudioInputTask(void const*) {
demodulatorTask();
break;
case STREAM_AMPLIFIED_INPUT_LEVEL:
- DEBUG("STREAM_AMPLIFIED_INPUT_LEVEL");
+ TNC_DEBUG("STREAM_AMPLIFIED_INPUT_LEVEL");
streamAmplifiedInputLevels();
break;
case POLL_AMPLIFIED_INPUT_LEVEL:
- DEBUG("POLL_AMPLIFIED_INPUT_LEVEL");
+ TNC_DEBUG("POLL_AMPLIFIED_INPUT_LEVEL");
pollAmplifiedInputLevel();
break;
#ifndef NUCLEOTNC
case POLL_BATTERY_LEVEL:
- DEBUG("POLL_BATTERY_LEVEL");
+ TNC_DEBUG("POLL_BATTERY_LEVEL");
pollBatteryLevel();
break;
#endif
case POLL_TWIST_LEVEL:
- DEBUG("POLL_TWIST_LEVEL");
+ TNC_DEBUG("POLL_TWIST_LEVEL");
pollInputTwist();
break;
case STREAM_AVERAGE_TWIST_LEVEL:
- DEBUG("STREAM_AVERAGE_TWIST_LEVEL");
+ TNC_DEBUG("STREAM_AVERAGE_TWIST_LEVEL");
// streamAverageInputTwist();
break;
case STREAM_INSTANT_TWIST_LEVEL:
- DEBUG("STREAM_INSTANT_TWIST_LEVEL");
+ TNC_DEBUG("STREAM_INSTANT_TWIST_LEVEL");
// streamInstantInputTwist();
break;
case AUTO_ADJUST_INPUT_LEVEL:
- DEBUG("AUTO_ADJUST_INPUT_LEVEL");
+ TNC_DEBUG("AUTO_ADJUST_INPUT_LEVEL");
autoAudioInputLevel();
break;
case CONFIGURE_INPUT_LEVELS:
- DEBUG("CONFIGURE_INPUT_LEVELS");
+ TNC_DEBUG("CONFIGURE_INPUT_LEVELS");
setAudioInputLevels();
break;
case UPDATE_SETTINGS:
- DEBUG("UPDATE_SETTINGS");
+ TNC_DEBUG("UPDATE_SETTINGS");
setAudioInputLevels();
updateModulator();
break;
@@ -190,7 +191,7 @@ IDemodulator* getDemodulator()
void demodulatorTask() {
- DEBUG("enter demodulatorTask");
+ TNC_DEBUG("enter demodulatorTask");
bool dcd_status{false};
auto demodulator = getDemodulator();
@@ -206,6 +207,8 @@ void demodulatorTask() {
continue;
}
+ HAL_IWDG_Refresh(&hiwdg);
+
auto block = (adc_pool_type::chunk_type*) evt.value.p;
auto samples = (int16_t*) block->buffer;
@@ -235,7 +238,7 @@ void demodulatorTask() {
demodulator->stop();
dcd_off();
- DEBUG("exit demodulatorTask");
+ TNC_DEBUG("exit demodulatorTask");
}
@@ -294,13 +297,13 @@ void streamLevels(uint8_t cmd) {
}
demodulator->stop();
- DEBUG("exit streamLevels");
+ TNC_DEBUG("exit streamLevels");
}
levels_type readLevels(uint32_t)
{
- DEBUG("enter readLevels");
+ TNC_DEBUG("enter readLevels");
// Return Vpp, Vavg, Vmin, Vmax as four 16-bit values, right justified.
@@ -382,7 +385,7 @@ float readTwist()
*/
void pollInputTwist()
{
- DEBUG("enter pollInputTwist");
+ TNC_DEBUG("enter pollInputTwist");
float g1200 = 0.0f;
float g2200 = 0.0f;
@@ -421,7 +424,7 @@ void pollInputTwist()
IDemodulator::stopADC();
- DEBUG("pollInputTwist: MARK=%d, SPACE=%d (x100)",
+ TNC_DEBUG("pollInputTwist: MARK=%d, SPACE=%d (x100)",
int(g1200 * 100.0 / AVG_SAMPLES), int(g2200 * 100.0 / AVG_SAMPLES));
int16_t g1200i = int16_t(g1200 * 256 / AVG_SAMPLES);
@@ -436,17 +439,17 @@ void pollInputTwist()
ioport->write(buffer, 5, 6, 10);
- DEBUG("exit pollInputTwist");
+ TNC_DEBUG("exit pollInputTwist");
}
void streamAmplifiedInputLevels() {
- DEBUG("enter streamAmplifiedInputLevels");
+ TNC_DEBUG("enter streamAmplifiedInputLevels");
streamLevels(kiss::hardware::POLL_INPUT_LEVEL);
- DEBUG("exit streamAmplifiedInputLevels");
+ TNC_DEBUG("exit streamAmplifiedInputLevels");
}
void pollAmplifiedInputLevel() {
- DEBUG("enter pollAmplifiedInputLevel");
+ TNC_DEBUG("enter pollAmplifiedInputLevel");
uint16_t Vpp, Vavg, Vmin, Vmax;
std::tie(Vpp, Vavg, Vmin, Vmax) = readLevels(AUDIO_IN);
@@ -468,7 +471,7 @@ void pollAmplifiedInputLevel() {
data[8] = (Vmax & 0xFF);
ioport->write(data, 9, 6, 10);
- DEBUG("exit pollAmplifiedInputLevel");
+ TNC_DEBUG("exit pollAmplifiedInputLevel");
}
#ifndef NUCLEOTNC
@@ -493,7 +496,7 @@ void stop() {
kiss::settings().input_offset += 6;
setAudioInputLevels();
kiss::settings().input_offset -= 6;
- DEBUG("Stop");
+ TNC_DEBUG("Stop");
// __disable_irq();
vTaskSuspendAll();
SysTick->CTRL = 0;
@@ -511,7 +514,7 @@ void stop() {
xTaskResumeAll();
setAudioInputLevels();
// adcState = DEMODULATOR;
- DEBUG("Wake");
+ TNC_DEBUG("Wake");
}
#endif
diff --git a/TNC/AudioInput.hpp b/TNC/AudioInput.hpp
index a070309..15d2903 100644
--- a/TNC/AudioInput.hpp
+++ b/TNC/AudioInput.hpp
@@ -29,10 +29,10 @@ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*);
* This is the long-running audio input task/thread. The ADC can be
* connected to one of 2 inputs:
*
- * - AUDIO_IN, ADC1, CHANNEL_8 -- this is the input used by the demodulator.
+ * - AMPLIFIED_AUDIO_IN, ADC1_IN7 -- this is the input used by the demodulator.
* - BATTERY_VOLTAGE -- this is 1/2 raw battery voltage (nominal 1.6-2.1V).
* This input should not be enabled unless BAT_DIVIDER is enabled and
- * pulled low. *** NOT USED on NucleoTNC ***
+ * pulled low.
*
* These inputs can be measured in a couple of different ways:
* - Vavg -- for the average DC level of the signal
diff --git a/TNC/AudioLevel.cpp b/TNC/AudioLevel.cpp
index 5c8ad92..4985c55 100644
--- a/TNC/AudioLevel.cpp
+++ b/TNC/AudioLevel.cpp
@@ -27,7 +27,7 @@ extern DAC_HandleTypeDef hdac1;
namespace mobilinkd { namespace tnc { namespace audio {
-uint16_t virtual_ground{0};
+int16_t virtual_ground{0};
float i_vgnd{0.0f};
void set_input_gain(int level)
diff --git a/TNC/AudioLevel.hpp b/TNC/AudioLevel.hpp
index 6f7533d..17f832e 100644
--- a/TNC/AudioLevel.hpp
+++ b/TNC/AudioLevel.hpp
@@ -1,8 +1,7 @@
-// Copyright 2015 Rob Riggs
+// Copyright 2015-2021 Rob Riggs
// All rights reserved.
-#ifndef MOBILINKD__TNC__AUDIO__AUDIO_LEVEL_H_
-#define MOBILINKD__TNC__AUDIO__AUDIO_LEVEL_H_
+#pragma once
#include "arm_math.h"
#include "cmsis_os.h"
@@ -29,9 +28,7 @@ void setAudioOutputLevel();
extern bool streamInputDCOffset;
constexpr const uint16_t vref = 4095; // Must match ADC output (adjust when oversampling)
-extern uint16_t virtual_ground;
+extern int16_t virtual_ground;
extern float i_vgnd;
}}} // mobilinkd::tnc::audio
-
-#endif // MOBILINKD__TNC__AUDIO__AUDIO_LEVEL_H_
diff --git a/TNC/Encoder.h b/TNC/Encoder.h
index d78a0d6..396275f 100644
--- a/TNC/Encoder.h
+++ b/TNC/Encoder.h
@@ -1,8 +1,10 @@
-// Copyright 2015-2020 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
#pragma once
+#include "HdlcFrame.hpp"
+
namespace mobilinkd
{
@@ -24,6 +26,7 @@ struct Encoder
virtual void updateModulator() = 0;
virtual void stop() = 0;
virtual EncoderType encoder_type() const = 0;
+ virtual tnc::hdlc::IoFrame* create_bert_frame() { return nullptr; };
};
} // mobilinkd
diff --git a/TNC/Fsk9600Demodulator.cpp b/TNC/Fsk9600Demodulator.cpp
index c6cd25e..7d36e55 100644
--- a/TNC/Fsk9600Demodulator.cpp
+++ b/TNC/Fsk9600Demodulator.cpp
@@ -69,7 +69,7 @@ hdlc::IoFrame* Fsk9600Demodulator::operator()(const q15_t* samples)
*/
float Fsk9600Demodulator::readTwist()
{
- DEBUG("enter Fsk9600Demodulator::readTwist");
+ TNC_DEBUG("enter Fsk9600Demodulator::readTwist");
float g120 = 0.0f;
float g4800 = 0.0f;
@@ -117,14 +117,14 @@ float Fsk9600Demodulator::readTwist()
INFO("9600 Twist = %d / 100 (%d - %d)", int(result * 100), int(g120 * 100),
int(g4800 * 100));
- DEBUG("exit Fsk9600Demodulator::readTwist");
+ TNC_DEBUG("exit Fsk9600Demodulator::readTwist");
return result;
}
uint32_t Fsk9600Demodulator::readBatteryLevel()
{
#ifndef NUCLEOTNC
- DEBUG("enter Fsk9600Demodulator::readBatteryLevel");
+ TNC_DEBUG("enter Fsk9600Demodulator::readBatteryLevel");
ADC_ChannelConfTypeDef sConfig;
@@ -155,21 +155,21 @@ uint32_t Fsk9600Demodulator::readBatteryLevel()
gpio::BAT_DIVIDER::off();
HAL_Delay(1);
- sConfig.Channel = ADC_CHANNEL_15;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+ sConfig.Channel = BATTERY_ADC_CHANNEL;
+ if (HAL_ADC_ConfigChannel(&BATTERY_ADC_HANDLE, &sConfig) != HAL_OK)
CxxErrorHandler();
uint32_t vbat = 0;
- if (HAL_ADC_Start(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Start(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
for (size_t i = 0; i != 8; ++i)
{
- if (HAL_ADC_PollForConversion(&hadc1, 1) != HAL_OK) CxxErrorHandler();
- vbat += HAL_ADC_GetValue(&hadc1);
+ if (HAL_ADC_PollForConversion(&BATTERY_ADC_HANDLE, 1) != HAL_OK) CxxErrorHandler();
+ vbat += HAL_ADC_GetValue(&BATTERY_ADC_HANDLE);
}
vbat /= 8;
- if (HAL_ADC_Stop(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Stop(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
CxxErrorHandler();
@@ -190,7 +190,7 @@ uint32_t Fsk9600Demodulator::readBatteryLevel()
INFO("Vref = %lumV", vref)
INFO("Vbat = %lumV", vbat);
- DEBUG("exit Fsk9600Demodulator::readBatteryLevel");
+ TNC_DEBUG("exit Fsk9600Demodulator::readBatteryLevel");
return vbat;
#else
return 0;
diff --git a/TNC/Fsk9600Modulator.hpp b/TNC/Fsk9600Modulator.hpp
index 26e03d2..59430ff 100644
--- a/TNC/Fsk9600Modulator.hpp
+++ b/TNC/Fsk9600Modulator.hpp
@@ -5,10 +5,14 @@
#include "Modulator.hpp"
+#include "stm32l4xx_hal.h"
+
#include
#include
#include
+extern IWDG_HandleTypeDef hiwdg;
+
namespace mobilinkd { namespace tnc {
struct Scrambler
@@ -86,7 +90,7 @@ struct Fsk9600Modulator : Modulator
case State::STOPPING:
case State::STOPPED:
ptt_->on();
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_DisableLSCO();
#endif
@@ -104,6 +108,8 @@ struct Fsk9600Modulator : Modulator
}
}
+ void tone(uint16_t freq) override {}
+
// DAC DMA interrupt functions.
void fill_first(uint8_t bit) override
@@ -128,6 +134,8 @@ struct Fsk9600Modulator : Modulator
void empty()
{
+ HAL_IWDG_Refresh(&hiwdg);
+
switch (state)
{
case State::STARTING:
@@ -140,7 +148,7 @@ struct Fsk9600Modulator : Modulator
stop_conversion();
ptt_->off();
level = Level::HIGH;
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
break;
@@ -155,7 +163,7 @@ struct Fsk9600Modulator : Modulator
stop_conversion();
ptt_->off();
level = Level::HIGH;
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
// Drain the queue.
@@ -204,6 +212,7 @@ private:
void fill(uint16_t* buffer, bool bit)
{
+ HAL_IWDG_Refresh(&hiwdg);
switch (level)
{
case Level::HIGH:
diff --git a/TNC/HdlcFrame.cpp b/TNC/HdlcFrame.cpp
index 084aeee..c1c94c8 100644
--- a/TNC/HdlcFrame.cpp
+++ b/TNC/HdlcFrame.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
#include "HdlcFrame.hpp"
diff --git a/TNC/HdlcFrame.hpp b/TNC/HdlcFrame.hpp
index 92ee303..e43569a 100644
--- a/TNC/HdlcFrame.hpp
+++ b/TNC/HdlcFrame.hpp
@@ -1,9 +1,7 @@
-// Copyright 2015-2019 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
-
-#ifndef MOBILINKD__HDLC_FRAME_HPP_
-#define MOBILINKD__HDLC_FRAME_HPP_
+#pragma once
#ifndef EXCLUDE_CRC
#include "stm32l4xx_hal.h"
@@ -39,7 +37,7 @@ public:
DATA = 0, TX_DELAY, P_PERSIST, SLOT_TIME, TX_TAIL, DUPLEX, HARDWARE,
TEXT, LOG};
- enum Source { RF_DATA = 0x80 };
+ enum Source { RF_DATA = 0x80, BERT = 0x30, STREAM = 0x20, PACKET = 0x10 };
private:
data_type data_;
@@ -70,7 +68,7 @@ private:
checksum <<= 16; // Shift
asm volatile("rbit %0, %0" : "+r" (checksum)); // Reverse
uint16_t result = checksum & 0xFFFF;
- DEBUG("CRC = %hx", result);
+ TNC_DEBUG("CRC = %hx", result);
return result;
}
#else
@@ -132,7 +130,7 @@ public:
fcs_ = (*it);
++it;
fcs_ |= (*it) << 8;
- DEBUG("FCS = %hx", fcs_);
+ TNC_DEBUG("FCS = %hx", fcs_);
crc_ = compute_crc(data_.begin());
complete_ = true;
}
@@ -207,5 +205,3 @@ IoFrame* acquire(void);
IoFrame* acquire_wait(void);
}}} // mobilinkd::tnc::hdlc
-
-#endif // MOBILINKD__HDLC_FRAME_HPP_
diff --git a/TNC/IOEventTask.cpp b/TNC/IOEventTask.cpp
index 1828115..e48f9e1 100644
--- a/TNC/IOEventTask.cpp
+++ b/TNC/IOEventTask.cpp
@@ -76,7 +76,7 @@ void startIOEventTask(void const*)
mobilinkd::tnc::audio::DEMODULATOR, osWaitForever);
break;
case CMD_USER_BUTTON_UP:
- DEBUG("Button Up");
+ TNC_DEBUG("Button Up");
break;
case CMD_SET_PTT_SIMPLEX:
getModulator().set_ptt(&simplexPtt);
@@ -97,7 +97,7 @@ void startIOEventTask(void const*)
if (frame->source() & IoFrame::RF_DATA)
{
- DEBUG("RF frame");
+ TNC_DEBUG("RF frame");
frame->source(frame->source() & 0x70);
if (!ioport->write(frame, frame->size() + 100))
{
@@ -108,7 +108,7 @@ void startIOEventTask(void const*)
}
else
{
- DEBUG("Serial frame");
+ TNC_DEBUG("Serial frame");
if ((frame->type() & 0x0F) == IoFrame::DATA)
{
kiss::getAFSKTestTone().stop();
diff --git a/TNC/IirFilter.hpp b/TNC/IirFilter.hpp
index 84394ca..d441147 100644
--- a/TNC/IirFilter.hpp
+++ b/TNC/IirFilter.hpp
@@ -1,4 +1,4 @@
-// Copyright 2015-2020 Rob Riggs
+// Copyright 2015-2021 Rob Riggs
// All rights reserved.
#pragma once
@@ -15,31 +15,32 @@ struct IirFilter {
const std::array& numerator_;
const std::array& denominator_;
std::array history_;
- size_t size_;
IirFilter(const std::array& b,
const std::array& a)
: numerator_(b), denominator_(a)
- , history_(), size_(N)
+ , history_()
{
history_.fill(0.0f);
}
~IirFilter() {}
+ static constexpr size_t size() { return N; }
+
float operator()(float input)
{
- for(size_t i = size_ - 1; i != 0; i--) history_[i] = history_[i - 1];
+ for (size_t i = N - 1; i != 0; i--) history_[i] = history_[i - 1];
history_[0] = input;
- for (size_t i = 1; i != size_; i++) {
+ for (size_t i = 1; i != N; i++) {
history_[0] -= denominator_[i] * history_[i];
}
float result = 0;
- for (size_t i = 0; i != size_; i++) {
+ for (size_t i = 0; i != N; i++) {
result += numerator_[i] * history_[i];
}
@@ -48,5 +49,3 @@ struct IirFilter {
};
}} // mobilinkd::tnc
-
-
diff --git a/TNC/Kiss.cpp b/TNC/Kiss.cpp
index eb96cd6..56e88be 100644
--- a/TNC/Kiss.cpp
+++ b/TNC/Kiss.cpp
@@ -20,55 +20,55 @@ void handle_frame(uint8_t frame_type, hdlc::IoFrame* frame) {
uint8_t value = *(frame->begin());
switch (frame_type) {
case kiss::FRAME_DATA:
- DEBUG("FRAME_DATA");
+ TNC_DEBUG("FRAME_DATA");
// osMessagePut(hdlcOutputQueueHandle, (uint32_t) frame, 0);
break;
case kiss::FRAME_TX_DELAY:
- DEBUG("FRAME_TX_DELAY");
+ TNC_DEBUG("FRAME_TX_DELAY");
kiss::settings().txdelay = value;
hdlc::release(frame);
updateModulator();
break;
case kiss::FRAME_P_PERSIST:
- DEBUG("FRAME_P_PERSIST");
+ TNC_DEBUG("FRAME_P_PERSIST");
kiss::settings().ppersist = value;
hdlc::release(frame);
updateModulator();
break;
case kiss::FRAME_SLOT_TIME:
- DEBUG("FRAME_SLOT_TIME");
+ TNC_DEBUG("FRAME_SLOT_TIME");
kiss::settings().slot = value;
hdlc::release(frame);
updateModulator();
break;
case kiss::FRAME_TX_TAIL:
- DEBUG("FRAME_TX_TAIL");
+ TNC_DEBUG("FRAME_TX_TAIL");
// Ignore.
hdlc::release(frame);
break;
case kiss::FRAME_DUPLEX:
- DEBUG("FRAME_DUPLEX");
+ TNC_DEBUG("FRAME_DUPLEX");
kiss::settings().duplex = value;
hdlc::release(frame);
updateModulator();
break;
case kiss::FRAME_HARDWARE:
- DEBUG("FRAME_HARDWARE");
+ TNC_DEBUG("FRAME_HARDWARE");
kiss::settings().handle_request(frame);
hdlc::release(frame);
break;
case kiss::FRAME_LOG:
- DEBUG("FRAME_LOG");
+ TNC_DEBUG("FRAME_LOG");
hdlc::release(frame);
// IGNORE
break;
case kiss::FRAME_RETURN:
- DEBUG("FRAME_RETURN");
+ TNC_DEBUG("FRAME_RETURN");
hdlc::release(frame);
// Leave KISS mode
break;
default:
- DEBUG("Unknown KISS frame type");
+ TNC_DEBUG("Unknown KISS frame type");
hdlc::release(frame);
}
}
diff --git a/TNC/KissHardware.cpp b/TNC/KissHardware.cpp
index 3776ff1..bdcc88e 100644
--- a/TNC/KissHardware.cpp
+++ b/TNC/KissHardware.cpp
@@ -42,14 +42,16 @@ int powerOffViaUSB(void)
namespace mobilinkd { namespace tnc { namespace kiss {
-#ifdef NUCLEOTNC
-const char FIRMWARE_VERSION[] = "2.3.0";
+#if defined(NUCLEOTNC)
+const char FIRMWARE_VERSION[] = "2.3.2";
const char HARDWARE_VERSION[] = "Mobilinkd NucleoTNC";
-#else
-const char FIRMWARE_VERSION[] = "2.3.0";
+#elif defined(STM32L433xx)
+const char FIRMWARE_VERSION[] = "2.3.2";
const char HARDWARE_VERSION[] = "Mobilinkd TNC3 2.1.1";
+#elif defined(STM32L4P5xx)
+const char FIRMWARE_VERSION[] = "2.3.2";
+const char HARDWARE_VERSION[] = "Mobilinkd TNC3+ Rev A";
#endif
-
Hardware& settings()
{
static Hardware instance __attribute__((section(".bss3")));
@@ -223,7 +225,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
reply8(hardware::SAVE_EEPROM_SETTINGS, hardware::OK);
break;
case hardware::POLL_INPUT_LEVEL:
- DEBUG("POLL_INPUT_VOLUME");
+ TNC_DEBUG("POLL_INPUT_VOLUME");
reply8(hardware::POLL_INPUT_LEVEL, 0);
osMessagePut(audioInputQueueHandle, audio::POLL_AMPLIFIED_INPUT_LEVEL,
osWaitForever);
@@ -231,43 +233,43 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
osWaitForever);
break;
case hardware::STREAM_INPUT_LEVEL:
- DEBUG("STREAM_INPUT_VOLUME");
+ TNC_DEBUG("STREAM_INPUT_VOLUME");
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
osWaitForever);
break;
case hardware::GET_BATTERY_LEVEL:
- DEBUG("GET_BATTERY_LEVEL");
+ TNC_DEBUG("GET_BATTERY_LEVEL");
osMessagePut(audioInputQueueHandle, audio::POLL_BATTERY_LEVEL,
osWaitForever);
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
osWaitForever);
break;
case hardware::SEND_MARK:
- DEBUG("SEND_MARK");
+ TNC_DEBUG("SEND_MARK");
osMessagePut(audioInputQueueHandle, audio::IDLE,
osWaitForever);
getAFSKTestTone().mark();
break;
case hardware::SEND_SPACE:
- DEBUG("SEND_SPACE");
+ TNC_DEBUG("SEND_SPACE");
osMessagePut(audioInputQueueHandle, audio::IDLE,
osWaitForever);
getAFSKTestTone().space();
break;
case hardware::SEND_BOTH:
- DEBUG("SEND_BOTH");
+ TNC_DEBUG("SEND_BOTH");
osMessagePut(audioInputQueueHandle, audio::IDLE,
osWaitForever);
getAFSKTestTone().both();
break;
case hardware::STOP_TX:
- DEBUG("STOP_TX");
+ TNC_DEBUG("STOP_TX");
getAFSKTestTone().stop();
osMessagePut(audioInputQueueHandle, audio::IDLE,
osWaitForever);
break;
case hardware::RESET:
- DEBUG("RESET");
+ TNC_DEBUG("RESET");
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
osWaitForever);
break;
@@ -275,21 +277,21 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
output_gain = *it << 8;
++it;
output_gain += *it;
- DEBUG("SET_OUTPUT_GAIN = %hd", output_gain);
+ TNC_DEBUG("SET_OUTPUT_GAIN = %hd", output_gain);
audio::setAudioOutputLevel();
update_crc();
[[fallthrough]];
case hardware::GET_OUTPUT_GAIN:
- DEBUG("GET_OUTPUT_GAIN");
+ TNC_DEBUG("GET_OUTPUT_GAIN");
reply16(hardware::GET_OUTPUT_GAIN, output_gain);
break;
case hardware::STREAM_DCD_VALUE:
- DEBUG("STREAM_DCD_VALUE");
+ TNC_DEBUG("STREAM_DCD_VALUE");
break;
case hardware::POLL_INPUT_TWIST:
- DEBUG("POLL_INPUT_TWIST");
+ TNC_DEBUG("POLL_INPUT_TWIST");
osMessagePut(audioInputQueueHandle, audio::POLL_TWIST_LEVEL,
osWaitForever);
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
@@ -297,19 +299,19 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
break;
case hardware::STREAM_AVG_INPUT_TWIST:
- DEBUG("STREAM_AVG_INPUT_TWIST");
+ TNC_DEBUG("STREAM_AVG_INPUT_TWIST");
osMessagePut(audioInputQueueHandle, audio::STREAM_AVERAGE_TWIST_LEVEL,
osWaitForever);
break;
case hardware::STREAM_INPUT_TWIST:
- DEBUG("STREAM_INPUT_TWIST");
+ TNC_DEBUG("STREAM_INPUT_TWIST");
osMessagePut(audioInputQueueHandle, audio::STREAM_INSTANT_TWIST_LEVEL,
osWaitForever);
break;
case hardware::ADJUST_INPUT_LEVELS:
- DEBUG("ADJUST_INPUT_LEVELS");
+ TNC_DEBUG("ADJUST_INPUT_LEVELS");
osMessagePut(audioInputQueueHandle, audio::AUTO_ADJUST_INPUT_LEVEL,
osWaitForever);
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
@@ -320,7 +322,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
input_gain = *it << 8;
++it;
input_gain += *it;
- DEBUG("SET_INPUT_GAIN = %d", input_gain);
+ TNC_DEBUG("SET_INPUT_GAIN = %d", input_gain);
update_crc();
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
osWaitForever);
@@ -328,12 +330,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
osWaitForever);
[[fallthrough]];
case hardware::GET_INPUT_GAIN:
- DEBUG("GET_INPUT_GAIN");
+ TNC_DEBUG("GET_INPUT_GAIN");
reply16(hardware::GET_INPUT_GAIN, input_gain);
break;
case hardware::SET_INPUT_TWIST:
- DEBUG("SET_INPUT_TWIST");
+ TNC_DEBUG("SET_INPUT_TWIST");
rx_twist = *it;
update_crc();
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
@@ -342,7 +344,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
osWaitForever);
[[fallthrough]];
case hardware::GET_INPUT_TWIST:
- DEBUG("GET_INPUT_TWIST");
+ TNC_DEBUG("GET_INPUT_TWIST");
reply8(hardware::GET_INPUT_TWIST, rx_twist);
break;
@@ -350,65 +352,65 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
tx_twist = *it;
if (tx_twist < 0) tx_twist = 0;
if (tx_twist > 100) tx_twist = 100;
- DEBUG("SET_OUTPUT_TWIST: %d", int(tx_twist));
+ TNC_DEBUG("SET_OUTPUT_TWIST: %d", int(tx_twist));
getModulator().init(*this);
update_crc();
[[fallthrough]];
case hardware::GET_OUTPUT_TWIST:
- DEBUG("GET_OUTPUT_TWIST");
+ TNC_DEBUG("GET_OUTPUT_TWIST");
reply8(hardware::GET_OUTPUT_TWIST, tx_twist);
break;
case hardware::STREAM_AMPLIFIED_INPUT:
- DEBUG("STREAM_AMPLIFIED_INPUT");
+ TNC_DEBUG("STREAM_AMPLIFIED_INPUT");
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
osWaitForever);
break;
case hardware::GET_TXDELAY:
- DEBUG("GET_TXDELAY");
+ TNC_DEBUG("GET_TXDELAY");
reply8(hardware::GET_TXDELAY, txdelay);
break;
case hardware::GET_PERSIST:
- DEBUG("GET_PERSIST");
+ TNC_DEBUG("GET_PERSIST");
reply8(hardware::GET_PERSIST, ppersist);
break;
case hardware::GET_TIMESLOT:
- DEBUG("GET_TIMESLOT");
+ TNC_DEBUG("GET_TIMESLOT");
reply8(hardware::GET_TIMESLOT, slot);
break;
case hardware::GET_TXTAIL:
- DEBUG("GET_TXTAIL");
+ TNC_DEBUG("GET_TXTAIL");
reply8(hardware::GET_TXTAIL, txtail);
break;
case hardware::GET_DUPLEX:
- DEBUG("GET_DUPLEX");
+ TNC_DEBUG("GET_DUPLEX");
reply8(hardware::GET_DUPLEX, duplex);
break;
case hardware::GET_FIRMWARE_VERSION:
- DEBUG("GET_FIRMWARE_VERSION");
+ TNC_DEBUG("GET_FIRMWARE_VERSION");
reply(hardware::GET_FIRMWARE_VERSION, (uint8_t*) FIRMWARE_VERSION,
sizeof(FIRMWARE_VERSION) - 1);
break;
case hardware::GET_HARDWARE_VERSION:
- DEBUG("GET_HARDWARE_VERSION");
+ TNC_DEBUG("GET_HARDWARE_VERSION");
reply(hardware::GET_HARDWARE_VERSION, (uint8_t*) HARDWARE_VERSION,
sizeof(HARDWARE_VERSION) - 1);
break;
case hardware::GET_SERIAL_NUMBER:
- DEBUG("GET_SERIAL_NUMBER");
+ TNC_DEBUG("GET_SERIAL_NUMBER");
reply(hardware::GET_SERIAL_NUMBER, (uint8_t*) serial_number_64,
sizeof(serial_number_64) - 1);
break;
case hardware::SET_PTT_CHANNEL:
- DEBUG("SET_PTT_CHANNEL");
+ TNC_DEBUG("SET_PTT_CHANNEL");
if (*it) {
options &= ~KISS_OPTION_PTT_SIMPLEX;
osMessagePut(ioEventQueueHandle, CMD_SET_PTT_MULTIPLEX, osWaitForever);
@@ -419,13 +421,13 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
break;
case hardware::GET_PTT_CHANNEL:
- DEBUG("GET_PTT_CHANNEL");
+ TNC_DEBUG("GET_PTT_CHANNEL");
reply8(hardware::GET_PTT_CHANNEL,
options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1);
break;
case hardware::SET_PASSALL:
- DEBUG("SET_PASSALL");
+ TNC_DEBUG("SET_PASSALL");
if (*it) {
options |= KISS_OPTION_PASSALL;
} else {
@@ -434,12 +436,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
[[fallthrough]];
case hardware::GET_PASSALL:
- DEBUG("GET_PASSALL");
+ TNC_DEBUG("GET_PASSALL");
reply8(hardware::GET_PASSALL, options & KISS_OPTION_PASSALL ? 1 : 0);
break;
case hardware::SET_RX_REV_POLARITY:
- DEBUG("SET_RX_REV_POLARITY");
+ TNC_DEBUG("SET_RX_REV_POLARITY");
if (*it) {
options |= KISS_OPTION_RX_REV_POLARITY;
} else {
@@ -448,12 +450,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
[[fallthrough]];
case hardware::GET_RX_REV_POLARITY:
- DEBUG("GET_RX_REV_POLARITY");
+ TNC_DEBUG("GET_RX_REV_POLARITY");
reply8(hardware::GET_RX_REV_POLARITY, options & KISS_OPTION_RX_REV_POLARITY ? 1 : 0);
break;
case hardware::SET_TX_REV_POLARITY:
- DEBUG("SET_TX_REV_POLARITY");
+ TNC_DEBUG("SET_TX_REV_POLARITY");
if (*it) {
options |= KISS_OPTION_TX_REV_POLARITY;
} else {
@@ -462,13 +464,13 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
[[fallthrough]];
case hardware::GET_TX_REV_POLARITY:
- DEBUG("GET_TX_REV_POLARITY");
+ TNC_DEBUG("GET_TX_REV_POLARITY");
reply8(hardware::GET_TX_REV_POLARITY, options & KISS_OPTION_TX_REV_POLARITY ? 1 : 0);
break;
#ifndef NUCLEOTNC
case hardware::SET_USB_POWER_OFF:
- DEBUG("SET_USB_POWER_OFF");
+ TNC_DEBUG("SET_USB_POWER_OFF");
if (*it) {
options |= KISS_OPTION_VIN_POWER_OFF;
} else {
@@ -477,13 +479,13 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
[[fallthrough]];
case hardware::GET_USB_POWER_OFF:
- DEBUG("GET_USB_POWER_OFF");
+ TNC_DEBUG("GET_USB_POWER_OFF");
reply8(hardware::GET_USB_POWER_OFF,
options & KISS_OPTION_VIN_POWER_OFF ? 1 : 0);
break;
case hardware::SET_USB_POWER_ON:
- DEBUG("SET_USB_POWER_ON");
+ TNC_DEBUG("SET_USB_POWER_ON");
if (*it) {
options |= KISS_OPTION_VIN_POWER_ON;
} else {
@@ -492,23 +494,23 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
update_crc();
[[fallthrough]];
case hardware::GET_USB_POWER_ON:
- DEBUG("GET_USB_POWER_ON");
+ TNC_DEBUG("GET_USB_POWER_ON");
reply8(hardware::GET_USB_POWER_ON,
options & KISS_OPTION_VIN_POWER_ON ? 1 : 0);
break;
#endif
case hardware::SET_DATETIME:
- DEBUG("SET_DATETIME");
+ TNC_DEBUG("SET_DATETIME");
set_rtc_datetime(static_cast(&*it));
[[fallthrough]];
case hardware::GET_DATETIME:
- DEBUG("GET_DATETIME");
+ TNC_DEBUG("GET_DATETIME");
reply(hardware::GET_DATETIME, get_rtc_datetime(), 7);
break;
case hardware::GET_CAPABILITIES:
- DEBUG("GET_CAPABILITIES");
+ TNC_DEBUG("GET_CAPABILITIES");
#ifndef NUCLEOTNC
reply16(hardware::GET_CAPABILITIES,
hardware::CAP_EEPROM_SAVE|hardware::CAP_BATTERY_LEVEL|
@@ -522,7 +524,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
break;
case hardware::GET_ALL_VALUES:
- DEBUG("GET_ALL_VALUES");
+ TNC_DEBUG("GET_ALL_VALUES");
// GET_API_VERSION must always come first.
reply16(hardware::GET_API_VERSION, hardware::KISS_API_VERSION);
#ifndef NUCLEOTNC
@@ -597,13 +599,13 @@ void Hardware::handle_ext_request(hdlc::IoFrame* frame) {
switch (ext_command) {
case hardware::EXT_SET_MODEM_TYPE[1]:
- DEBUG("SET_MODEM_TYPE");
+ TNC_DEBUG("SET_MODEM_TYPE");
if ((*it == hardware::MODEM_TYPE_1200)
or (*it == hardware::MODEM_TYPE_9600)
or (*it == hardware::MODEM_TYPE_M17))
{
modem_type = *it;
- DEBUG(modem_type_lookup[modem_type]);
+ TNC_DEBUG(modem_type_lookup[modem_type]);
update_crc();
}
else
@@ -615,11 +617,11 @@ void Hardware::handle_ext_request(hdlc::IoFrame* frame) {
osWaitForever);
[[fallthrough]];
case hardware::EXT_GET_MODEM_TYPE[1]:
- DEBUG("EXT_GET_MODEM_TYPE");
+ TNC_DEBUG("EXT_GET_MODEM_TYPE");
ext_reply(hardware::EXT_GET_MODEM_TYPE, modem_type);
break;
case hardware::EXT_GET_MODEM_TYPES[1]:
- DEBUG("EXT_GET_MODEM_TYPES");
+ TNC_DEBUG("EXT_GET_MODEM_TYPES");
ext_reply(hardware::EXT_GET_MODEM_TYPES, supported_modem_types);
break;
default:
@@ -667,7 +669,7 @@ bool I2C_Storage::load(void* ptr, size_t len)
{
if (HAL_I2C_Init(&eeprom_i2c) != HAL_OK) CxxErrorHandler();
- DEBUG("Attempting to read %d bytes from EEPROM...", len);
+ TNC_DEBUG("Attempting to read %d bytes from EEPROM...", len);
uint32_t timeout = 1000; // systicks... milliseconds
diff --git a/TNC/KissHardware.hpp b/TNC/KissHardware.hpp
index 22b2dbe..a998258 100644
--- a/TNC/KissHardware.hpp
+++ b/TNC/KissHardware.hpp
@@ -6,6 +6,7 @@
#include "Log.h"
#include "HdlcFrame.hpp"
#include "AFSKTestTone.hpp"
+#include "main.h"
#include
#include
@@ -329,44 +330,44 @@ struct Hardware
memset(beacons, 0, sizeof(beacons));
update_crc();
- DEBUG("Settings initialized");
+ TNC_DEBUG("Settings initialized");
}
void debug() {
#ifdef KISS_LOGGING
- DEBUG("Hardware Settings (size=%d):", sizeof(Hardware));
- DEBUG("TX Delay: %d", (int)txdelay);
- DEBUG("P* Persistence: %d", (int)ppersist);
- DEBUG("Slot Time: %d", (int)slot);
- DEBUG("TX Tail: %d", (int)txtail);
- DEBUG("Duplex: %d", (int)duplex);
- DEBUG("Modem Type: %s", modem_type_lookup[modem_type]);
- DEBUG("TX Gain: %d", (int)output_gain);
- DEBUG("RX Gain: %d", (int)input_gain);
- DEBUG("TX Twist: %d", (int)tx_twist);
- DEBUG("RX Twist: %d", (int)rx_twist);
- DEBUG("Log Level: %d", (int)log_level);
- DEBUG("Options: %04hx", options);
- DEBUG("MYCALL: %s", mycall.data());
- DEBUG("Dedupe time (secs): %d", (int)dedupe_seconds);
- DEBUG("Aliases:");
+ TNC_DEBUG("Hardware Settings (size=%d):", sizeof(Hardware));
+ TNC_DEBUG("TX Delay: %d", (int)txdelay);
+ TNC_DEBUG("P* Persistence: %d", (int)ppersist);
+ TNC_DEBUG("Slot Time: %d", (int)slot);
+ TNC_DEBUG("TX Tail: %d", (int)txtail);
+ TNC_DEBUG("Duplex: %d", (int)duplex);
+ TNC_DEBUG("Modem Type: %s", modem_type_lookup[modem_type]);
+ TNC_DEBUG("TX Gain: %d", (int)output_gain);
+ TNC_DEBUG("RX Gain: %d", (int)input_gain);
+ TNC_DEBUG("TX Twist: %d", (int)tx_twist);
+ TNC_DEBUG("RX Twist: %d", (int)rx_twist);
+ TNC_DEBUG("Log Level: %d", (int)log_level);
+ TNC_DEBUG("Options: %04hx", options);
+ TNC_DEBUG("MYCALL: %s", mycall.data());
+ TNC_DEBUG("Dedupe time (secs): %d", (int)dedupe_seconds);
+ TNC_DEBUG("Aliases:");
for (auto& a : aliases) {
if (!a.set) continue;
- DEBUG(" call: %s", a.call.data());
- DEBUG(" use: %d", (int)a.use);
- DEBUG(" insert: %d", (int)a.insert_id);
- DEBUG(" preempt: %d", (int)a.preempt);
- DEBUG(" hops: %d", (int)a.hops);
+ TNC_DEBUG(" call: %s", a.call.data());
+ TNC_DEBUG(" use: %d", (int)a.use);
+ TNC_DEBUG(" insert: %d", (int)a.insert_id);
+ TNC_DEBUG(" preempt: %d", (int)a.preempt);
+ TNC_DEBUG(" hops: %d", (int)a.hops);
}
- DEBUG("Beacons:");
+ TNC_DEBUG("Beacons:");
for (auto& b : this->beacons) {
if (b.seconds == 0) continue;
- DEBUG(" dest: %s", b.dest.data());
- DEBUG(" path: %s", (char*)b.path);
- DEBUG(" text: %s", (char*)b.text);
- DEBUG(" frequency (secs): %d", (int)b.seconds);
+ TNC_DEBUG(" dest: %s", b.dest.data());
+ TNC_DEBUG(" path: %s", (char*)b.path);
+ TNC_DEBUG(" text: %s", (char*)b.text);
+ TNC_DEBUG(" frequency (secs): %d", (int)b.seconds);
}
- DEBUG("Checksum: %04hx", checksum);
+ TNC_DEBUG("Checksum: %04hx", checksum);
#endif
}
diff --git a/TNC/LEDIndicator.cpp b/TNC/LEDIndicator.cpp
index 94a06a5..719afaa 100644
--- a/TNC/LEDIndicator.cpp
+++ b/TNC/LEDIndicator.cpp
@@ -14,7 +14,7 @@
#include
-extern TIM_HandleTypeDef htim1;
+extern TIM_HandleTypeDef LED_PWM_TIMER_HANDLE;
namespace mobilinkd {
namespace tnc {
@@ -421,7 +421,7 @@ struct Flash
if (counter == 0)
{
state = STATE::OFF;
- HAL_TIM_PWM_Stop(&htim1, channel);
+ HAL_TIM_PWM_Stop(&LED_PWM_TIMER_HANDLE, channel);
}
else
{
@@ -451,7 +451,7 @@ struct Flash
auto expected = STATE::OFF;
if (gr_state.compare_exchange_strong(expected, STATE::RAMP_UP))
{
- HAL_TIM_PWM_Start(&htim1, GREEN_CHANNEL);
+ HAL_TIM_PWM_Start(&LED_PWM_TIMER_HANDLE, GREEN_CHANNEL);
}
else
{
@@ -473,7 +473,7 @@ struct Flash
if (rd_state.compare_exchange_strong(expected, STATE::RAMP_UP))
{
// PWM Channel must match
- HAL_TIM_PWM_Start(&htim1, RED_CHANNEL);
+ HAL_TIM_PWM_Start(&LED_PWM_TIMER_HANDLE, RED_CHANNEL);
}
else
{
@@ -492,19 +492,19 @@ struct Flash
void disconnect()
{
blue_func = noConnection;
- HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
+ HAL_TIM_PWM_Start(&LED_PWM_TIMER_HANDLE, BLUE_CHANNEL);
}
void usb()
{
blue_func = usbConnection;
- HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
+ HAL_TIM_PWM_Start(&LED_PWM_TIMER_HANDLE, BLUE_CHANNEL);
}
void bt()
{
blue_func = btConnection;
- HAL_TIM_PWM_Start(&htim1, BLUE_CHANNEL);
+ HAL_TIM_PWM_Start(&LED_PWM_TIMER_HANDLE, BLUE_CHANNEL);
}
};
@@ -518,25 +518,25 @@ Flash& flash()
}
} // mobilinkd::tnc
-void HTIM1_PeriodElapsedCallback()
+void LED_TIMER_PeriodElapsedCallback()
{
using mobilinkd::tnc::flash;
// CCR registers must match the TIM_CHANNEL used for each LED in Flash.
#ifndef NUCLEOTNC
- htim1.Instance->CCR1 = flash().blue();
- htim1.Instance->CCR2 = flash().green();
- htim1.Instance->CCR3 = flash().red();
+ LED_PWM_TIMER_HANDLE.Instance->CCR1 = flash().blue();
+ LED_PWM_TIMER_HANDLE.Instance->CCR2 = flash().green();
+ LED_PWM_TIMER_HANDLE.Instance->CCR3 = flash().red();
#else
- htim1.Instance->CCR1 = flash().red();
- htim1.Instance->CCR2 = flash().green();
- htim1.Instance->CCR3 = flash().blue(); // YELLOW
+ LED_PWM_TIMER_HANDLE.Instance->CCR1 = flash().red();
+ LED_PWM_TIMER_HANDLE.Instance->CCR2 = flash().green();
+ LED_PWM_TIMER_HANDLE.Instance->CCR3 = flash().blue(); // YELLOW
#endif
}
void indicate_turning_on(void)
{
- HAL_TIM_Base_Start_IT(&htim1);
+ HAL_TIM_Base_Start_IT(&LED_PWM_TIMER_HANDLE);
tx_on();
rx_on();
}
diff --git a/TNC/LEDIndicator.h b/TNC/LEDIndicator.h
index d94b768..624c917 100644
--- a/TNC/LEDIndicator.h
+++ b/TNC/LEDIndicator.h
@@ -19,7 +19,7 @@ void tx_off(void);
void rx_on(void);
void rx_off(void);
-void HTIM1_PeriodElapsedCallback(void);
+void LED_TIMER_PeriodElapsedCallback(void);
#ifdef __cplusplus
}
diff --git a/TNC/Log.h b/TNC/Log.h
index 85e8ca2..12dcb6b 100644
--- a/TNC/Log.h
+++ b/TNC/Log.h
@@ -28,14 +28,12 @@ void log_(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3)))
#define LOG(level, ...) if(level >= KISS_LOG_LEVEL) log_(level, __VA_ARGS__);
-#define DEBUG(...) LOG(0, __VA_ARGS__)
#define TNC_DEBUG(...) LOG(0, __VA_ARGS__)
#define INFO(...) LOG(1, __VA_ARGS__)
#define WARN(...) LOG(2, __VA_ARGS__)
#define ERROR(...) LOG(3, __VA_ARGS__)
#define SEVERE(...) LOG(4, __VA_ARGS__)
#else
-#define DEBUG(...)
#define TNC_DEBUG(...)
#define INFO(...)
#define WARN(...)
diff --git a/TNC/M17.h b/TNC/M17.h
index ae8450a..a903a29 100644
--- a/TNC/M17.h
+++ b/TNC/M17.h
@@ -27,6 +27,7 @@ extern const std::array rrc_taps_f15;
constexpr std::array LSF_SYNC = { 0x55, 0xF7 };
constexpr std::array STREAM_SYNC = { 0xFF, 0x5D };
constexpr std::array PACKET_SYNC = { 0x75, 0xFF };
+constexpr std::array BERT_SYNC = { 0xDF, 0x55 };
inline constexpr uint16_t sync_word(std::array sw)
{
diff --git a/TNC/M17Demodulator.cpp b/TNC/M17Demodulator.cpp
index 2a3b21c..c60f86f 100644
--- a/TNC/M17Demodulator.cpp
+++ b/TNC/M17Demodulator.cpp
@@ -16,13 +16,15 @@
namespace mobilinkd { namespace tnc {
//m17::Indicator lsf_indicator{GPIOB, GPIO_PIN_0};
-//m17::Indicator dcd_indicator{GPIOA, GPIO_PIN_7};
-//m17::Indicator str_indicator{GPIOA, GPIO_PIN_2};
+//m17::Indicator dcd_indicator{GPIOA, GPIO_PIN_2};
+//m17::Indicator str_indicator{GPIOA, GPIO_PIN_7};
void M17Demodulator::start()
{
SysClock72();
+#if defined(HAVE_LSCO)
HAL_RCCEx_DisableLSCO();
+#endif
demod_filter.init(m17::rrc_taps_f15);
passall(kiss::settings().options & KISS_OPTION_PASSALL);
@@ -53,31 +55,31 @@ void M17Demodulator::start()
void M17Demodulator::update_values(uint8_t index)
{
- correlator.apply([this,index](float t){dev.sample(t);}, index);
- dev.update();
- idev = dev.idev() * polarity;
- sync_sample_index = index;
+ correlator.apply([this,index](float t){dev.sample(t);}, index);
+ dev.update();
+ idev = dev.idev() * polarity;
+ sync_sample_index = index;
}
void M17Demodulator::dcd_on()
{
- // Data carrier newly detected.
- INFO("dcd = %d", int(dcd.level() * 100));
- dcd_ = true;
- sync_count = 0;
- dev.reset();
+ // Data carrier newly detected.
+ INFO("dcd = %d", int(dcd.level() * 100));
+ dcd_ = true;
+ sync_count = 0;
+ dev.reset();
framer.reset();
decoder.reset();
- missing_sync_count = 0;
+ missing_sync_count = 0;
// dcd_indicator.off();
}
void M17Demodulator::dcd_off()
{
- // Just lost data carrier.
- INFO("dcd = %d", int(dcd.level() * 100));
- dcd_ = false;
- demodState = DemodState::UNLOCKED;
+ // Just lost data carrier.
+ INFO("dcd = %d", int(dcd.level() * 100));
+ dcd_ = false;
+ demodState = DemodState::UNLOCKED;
// dcd_indicator.on();
adc_timing_adjust = 0;
prev_clock_estimate = 1.;
@@ -95,8 +97,8 @@ void M17Demodulator::initialize(const q15_t* input)
auto filtered = demod_filter(demod_buffer.data());
for (size_t i = 0; i != ADC_BLOCK_SIZE; ++i)
{
- auto filtered_sample = filtered[i];
- correlator.sample(filtered_sample);
+ auto filtered_sample = filtered[i];
+ correlator.sample(filtered_sample);
}
}
@@ -106,66 +108,79 @@ void M17Demodulator::update_dcd(const q15_t* input)
if (!dcd_ && dcd.dcd())
{
- dcd_on();
- need_clock_reset_ = true;
+ dcd_on();
+ need_clock_reset_ = true;
}
else if (dcd_ && !dcd.dcd())
{
- dcd_off();
+ dcd_off();
}
for (size_t i = 0; i != ADC_BLOCK_SIZE; ++i)
{
- dcd(input[i] * inv);
+ dcd(input[i] * inv);
}
}
[[gnu::noinline]]
void M17Demodulator::do_unlocked()
{
- // We expect to find the preamble immediately after DCD.
+ // We expect to find the preamble immediately after DCD.
if (missing_sync_count < 1920)
- {
- missing_sync_count += 1;
- auto sync_index = preamble_sync(correlator);
- auto sync_updated = preamble_sync.updated();
- if (sync_updated)
- {
- sync_count = 0;
- missing_sync_count = 0;
- need_clock_reset_ = true;
- dev.reset();
- update_values(sync_index);
- sample_index = sync_index;
- demodState = DemodState::LSF_SYNC;
- INFO("P sync %d", sync_index);
- }
- }
- else // Otherwise we start searching for a sync word.
- {
- auto sync_index = lsf_sync(correlator);
- auto sync_updated = lsf_sync.updated();
- if (sync_updated)
- {
- sync_count = 0;
- missing_sync_count = 0;
- need_clock_reset_ = true;
- dev.reset();
- update_values(sync_index);
- sample_index = sync_index;
- demodState = DemodState::FRAME;
- if (sync_updated < 0)
- {
- sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
- INFO("S sync %d", int(sync_index));
- }
- else
- {
- sync_word_type = M17FrameDecoder::SyncWordType::LSF;
- INFO("L sync %d", int(sync_index));
- }
- }
- }
+ {
+ missing_sync_count += 1;
+ auto sync_index = preamble_sync(correlator);
+ auto sync_updated = preamble_sync.updated();
+ if (sync_updated)
+ {
+ sync_count = 0;
+ missing_sync_count = 0;
+ need_clock_reset_ = true;
+ dev.reset();
+ update_values(sync_index);
+ sample_index = sync_index;
+ demodState = DemodState::LSF_SYNC;
+ INFO("P sync %d", sync_index);
+ }
+ return;
+ }
+ // Otherwise we start searching for a sync word.
+ auto sync_index = lsf_sync(correlator);
+ auto sync_updated = lsf_sync.updated();
+ if (sync_updated)
+ {
+ sync_count = 0;
+ missing_sync_count = 0;
+ need_clock_reset_ = true;
+ dev.reset();
+ update_values(sync_index);
+ sample_index = sync_index;
+ demodState = DemodState::FRAME;
+ if (sync_updated < 0)
+ {
+ sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
+ INFO("S sync %d", int(sync_index));
+ }
+ else
+ {
+ sync_word_type = M17FrameDecoder::SyncWordType::LSF;
+ INFO("L sync %d", int(sync_index));
+ }
+ }
+ sync_index = packet_sync(correlator);
+ sync_updated = packet_sync.updated();
+ if (sync_updated < 0)
+ {
+ sync_count = 0;
+ missing_sync_count = 0;
+ need_clock_reset_ = true;
+ dev.reset();
+ update_values(sync_index);
+ sample_index = sync_index;
+ demodState = DemodState::FRAME;
+ sync_word_type = M17FrameDecoder::SyncWordType::BERT;
+ INFO("B sync %d", int(sync_index));
+ }
}
/**
@@ -176,48 +191,59 @@ void M17Demodulator::do_unlocked()
[[gnu::noinline]]
void M17Demodulator::do_lsf_sync()
{
- float sync_triggered = 0.;
+ float sync_triggered = 0.;
+ float bert_triggered = 0.;
- if (correlator.index() == sample_index)
- {
- sync_triggered = preamble_sync.triggered(correlator);
+ if (correlator.index() == sample_index)
+ {
+ sync_triggered = preamble_sync.triggered(correlator);
// INFO("PSync = %d", int(sync_triggered));
- if (sync_triggered > 0.1)
- {
- ITM_SendChar('.');
- return;
- }
- sync_triggered = lsf_sync.triggered(correlator);
- if (std::abs(sync_triggered) > 0.1)
- {
- missing_sync_count = 0;
- need_clock_update_ = true;
- update_values(sample_index);
- INFO("LSync = %d", int(sync_triggered));
- if (sync_triggered > 0)
- {
- demodState = DemodState::FRAME;
- sync_word_type = M17FrameDecoder::SyncWordType::LSF;
- INFO("+l sync %d", int(sample_index));
- }
- else
- {
- demodState = DemodState::FRAME;
- sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
- INFO("+s sync %d", int(sample_index));
- }
- }
- else if (++missing_sync_count > 192)
- {
- demodState = DemodState::UNLOCKED;
- INFO("l unlock %d", int(missing_sync_count));
- missing_sync_count = 0;
- }
- else
- {
- update_values(sample_index);
- }
- }
+ if (sync_triggered > 0.1)
+ {
+ ITM_SendChar('.');
+ return;
+ }
+ sync_triggered = lsf_sync.triggered(correlator);
+ bert_triggered = packet_sync.triggered(correlator);
+ if (bert_triggered < 0)
+ {
+ missing_sync_count = 0;
+ need_clock_update_ = true;
+ update_values(sample_index);
+ demodState = DemodState::FRAME;
+ sync_word_type = M17FrameDecoder::SyncWordType::BERT;
+ INFO("+b sync");
+ }
+ else if (std::abs(sync_triggered) > 0.1)
+ {
+ missing_sync_count = 0;
+ need_clock_update_ = true;
+ update_values(sample_index);
+ INFO("LSync = %d", int(sync_triggered));
+ if (sync_triggered > 0)
+ {
+ demodState = DemodState::FRAME;
+ sync_word_type = M17FrameDecoder::SyncWordType::LSF;
+ INFO("+l sync %d", int(sample_index));
+ }
+ else
+ {
+ demodState = DemodState::FRAME;
+ sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
+ INFO("+s sync %d", int(sample_index));
+ }
+ }
+ else if (++missing_sync_count > 192)
+ {
+ demodState = DemodState::UNLOCKED;
+ INFO("l unlock %d", int(missing_sync_count));
+ missing_sync_count = 0;
+ }
+ else
+ {
+ update_values(sample_index);
+ }
+ }
}
/**
@@ -229,43 +255,36 @@ void M17Demodulator::do_lsf_sync()
[[gnu::noinline]]
void M17Demodulator::do_stream_sync()
{
- uint8_t sync_index = lsf_sync(correlator);
- int8_t sync_updated = lsf_sync.updated();
- sync_count += 1;
- if (sync_updated < 0)
- {
- missing_sync_count = 0;
- if (sync_count <= 70)
- {
- update_values(sync_index);
- sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
- demodState = DemodState::FRAME;
- INFO("s early");
- }
- else if (sync_count > 70)
- {
- update_values(sync_index);
- sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
- demodState = DemodState::FRAME;
- INFO("s sync %d", int(sync_index));
- }
- return;
- }
+ uint8_t sync_index = lsf_sync(correlator);
+ int8_t sync_updated = lsf_sync.updated();
+ sync_count += 1;
+ if (sync_updated < 0)
+ {
+ missing_sync_count = 0;
+ if (sync_count > 70)
+ {
+ update_values(sync_index);
+ sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
+ demodState = DemodState::FRAME;
+ INFO("s sync %d", int(sync_index));
+ }
+ return;
+ }
else if (sync_count > 87)
{
- update_values(sync_index);
- missing_sync_count += 1;
- if (missing_sync_count < MAX_MISSING_SYNC)
- {
- sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
- demodState = DemodState::FRAME;
- INFO("s unsync %d", int(missing_sync_count));
- }
- else
- {
- demodState = DemodState::LSF_SYNC;
- INFO("s unlock");
- }
+ update_values(sync_index);
+ missing_sync_count += 1;
+ if (missing_sync_count < MAX_MISSING_SYNC)
+ {
+ sync_word_type = M17FrameDecoder::SyncWordType::STREAM;
+ demodState = DemodState::FRAME;
+ INFO("s unsync %d", int(missing_sync_count));
+ }
+ else
+ {
+ demodState = DemodState::LSF_SYNC;
+ INFO("s unlock");
+ }
}
}
@@ -276,100 +295,137 @@ void M17Demodulator::do_stream_sync()
[[gnu::noinline]]
void M17Demodulator::do_packet_sync()
{
- auto sync_index = packet_sync(correlator);
- auto sync_updated = packet_sync.updated();
- sync_count += 1;
- if (sync_count > 70 && sync_updated)
- {
- missing_sync_count = 0;
- update_values(sync_index);
- sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
- demodState = DemodState::FRAME;
- INFO("k sync");
- return;
- }
- else if (sync_count > 87)
- {
- missing_sync_count += 1;
- if (missing_sync_count < MAX_MISSING_SYNC)
- {
- sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
- demodState = DemodState::FRAME;
- INFO("k unsync");
- }
- else
- {
- demodState = DemodState::UNLOCKED;
- INFO("k unlock");
- }
- }
+ auto sync_index = packet_sync(correlator);
+ auto sync_updated = packet_sync.updated();
+ sync_count += 1;
+ if (sync_count > 70 && sync_updated)
+ {
+ missing_sync_count = 0;
+ update_values(sync_index);
+ sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
+ demodState = DemodState::FRAME;
+ INFO("k sync");
+ return;
+ }
+ else if (sync_count > 87)
+ {
+ missing_sync_count += 1;
+ if (missing_sync_count < MAX_MISSING_SYNC)
+ {
+ sync_word_type = M17FrameDecoder::SyncWordType::PACKET;
+ demodState = DemodState::FRAME;
+ INFO("k unsync");
+ }
+ else
+ {
+ demodState = DemodState::UNLOCKED;
+ INFO("k unlock");
+ }
+ }
+}
+
+/**
+ * Check for a bert sync word.
+ */
+[[gnu::noinline]]
+void M17Demodulator::do_bert_sync()
+{
+ auto sync_index = packet_sync(correlator);
+ auto sync_updated = packet_sync.updated();
+ sync_count += 1;
+ if (sync_count > 70 && sync_updated < 0)
+ {
+ missing_sync_count = 0;
+ update_values(sync_index);
+ sync_word_type = M17FrameDecoder::SyncWordType::BERT;
+ INFO("b sync");
+ demodState = DemodState::FRAME;
+ }
+ else if (sync_count > 87)
+ {
+ missing_sync_count += 1;
+ if (missing_sync_count < MAX_MISSING_SYNC)
+ {
+ sync_word_type = M17FrameDecoder::SyncWordType::BERT;
+ demodState = DemodState::FRAME;
+ INFO("b unsync");
+ }
+ else
+ {
+ demodState = DemodState::UNLOCKED;
+ INFO("b unlock");
+ }
+ }
}
[[gnu::noinline]]
void M17Demodulator::do_frame(float filtered_sample, hdlc::IoFrame*& frame_result)
{
- if (correlator.index() != sample_index) return;
+ if (correlator.index() != sample_index) return;
- float sample = filtered_sample - dev.offset();
- sample *= idev;
+ float sample = filtered_sample - dev.offset();
+ sample *= idev;
- auto n = mobilinkd::llr(sample);
- int8_t* tmp;
- auto len = framer(n, &tmp);
- if (len != 0)
- {
- need_clock_update_ = true;
+ auto n = mobilinkd::llr(sample);
+ int8_t* tmp;
+ auto len = framer(n, &tmp);
+ if (len != 0)
+ {
+ need_clock_update_ = true;
- std::copy(tmp, tmp + len, buffer.begin());
- auto valid = decoder(sync_word_type, buffer, frame_result, ber);
- INFO("demod: %d, dt: %7d, evma: %5d, dev: %5d, freq: %5d, index: %d, %d, %d ber: %d",
- int(decoder.state()), int(clock_recovery.clock_estimate() * 1000000),
- int(dev.error() * 1000), int(dev.deviation() * 1000),
- int(dev.offset() * 1000),
- int(sample_index), int(clock_recovery.sample_index()), int(sync_sample_index),
- ber);
+ std::copy(tmp, tmp + len, buffer.begin());
+ auto valid = decoder(sync_word_type, buffer, frame_result, ber);
+ INFO("demod: %d, dt: %7d, evma: %5d, dev: %5d, freq: %5d, index: %d, %d, %d ber: %d",
+ int(decoder.state()), int(clock_recovery.clock_estimate() * 1000000),
+ int(dev.error() * 1000), int(dev.deviation() * 1000),
+ int(dev.offset() * 1000),
+ int(sample_index), int(clock_recovery.sample_index()), int(sync_sample_index),
+ ber);
- switch (decoder.state())
- {
- case M17FrameDecoder::State::STREAM:
- demodState = DemodState::STREAM_SYNC;
- break;
- case M17FrameDecoder::State::LSF:
- // If state == LSF, we need to recover LSF from LICH.
- demodState = DemodState::STREAM_SYNC;
- break;
- default:
- demodState = DemodState::PACKET_SYNC;
- break;
- }
+ switch (decoder.state())
+ {
+ case M17FrameDecoder::State::STREAM:
+ demodState = DemodState::STREAM_SYNC;
+ break;
+ case M17FrameDecoder::State::LSF:
+ // If state == LSF, we need to recover LSF from LICH.
+ demodState = DemodState::STREAM_SYNC;
+ break;
+ case M17FrameDecoder::State::BERT:
+ demodState = DemodState::BERT_SYNC;
+ break;
+ default:
+ demodState = DemodState::PACKET_SYNC;
+ break;
+ }
- sync_count = 0;
+ sync_count = 0;
- switch (valid)
- {
- case M17FrameDecoder::DecodeResult::FAIL:
- WARN("decode invalid");
- if (frame_result && !passall_)
- {
- hdlc::release(frame_result);
- frame_result = nullptr;
- }
- break;
- case M17FrameDecoder::DecodeResult::EOS:
- demodState = DemodState::LSF_SYNC;
- INFO("EOS");
- break;
- case M17FrameDecoder::DecodeResult::OK:
- break;
- case M17FrameDecoder::DecodeResult::INCOMPLETE:
- break;
- }
- }
+ switch (valid)
+ {
+ case M17FrameDecoder::DecodeResult::FAIL:
+ WARN("decode invalid");
+ if (frame_result && !passall_)
+ {
+ hdlc::release(frame_result);
+ frame_result = nullptr;
+ }
+ break;
+ case M17FrameDecoder::DecodeResult::EOS:
+ demodState = DemodState::LSF_SYNC;
+ INFO("EOS");
+ break;
+ case M17FrameDecoder::DecodeResult::OK:
+ break;
+ case M17FrameDecoder::DecodeResult::INCOMPLETE:
+ break;
+ }
+ }
}
hdlc::IoFrame* M17Demodulator::operator()(const q15_t* input)
{
- static int16_t initializing = 10;
+ static int16_t initializing = 10;
hdlc::IoFrame* frame_result = nullptr;
@@ -377,12 +433,12 @@ hdlc::IoFrame* M17Demodulator::operator()(const q15_t* input)
// the demodulator.
if (__builtin_expect((initializing), 0))
{
- --initializing;
- initialize(input);
- return frame_result;
+ --initializing;
+ initialize(input);
+ return frame_result;
}
-// str_indicator.on();
+// str_indicator.on();
update_dcd(input);
if (!dcd_)
@@ -411,55 +467,58 @@ hdlc::IoFrame* M17Demodulator::operator()(const q15_t* input)
if (correlator.index() == 0)
{
- if (need_clock_reset_)
- {
- clock_recovery.reset();
- need_clock_reset_ = false;
- sample_index = sync_sample_index;
- adc_timing_adjust = 0;
- }
- else if (need_clock_update_) // must avoid update immediately after reset.
- {
- clock_recovery.update();
- uint8_t clock_index = clock_recovery.sample_index();
- uint8_t clock_diff = std::abs(sample_index - clock_index);
- uint8_t sync_diff = std::abs(sample_index - sync_sample_index);
- bool clock_diff_ok = clock_diff <= 1 || clock_diff == 9;
- bool sync_diff_ok = sync_diff <= 1 || sync_diff == 9;
- if (clock_diff_ok) sample_index = clock_index;
- else if (sync_diff_ok) sample_index = sync_sample_index;
- // else unchanged.
- need_clock_update_ = false;
- }
+ if (need_clock_reset_)
+ {
+ clock_recovery.reset();
+ need_clock_reset_ = false;
+ sample_index = sync_sample_index;
+ adc_timing_adjust = 0;
+ }
+ else if (need_clock_update_) // must avoid update immediately after reset.
+ {
+ clock_recovery.update();
+ uint8_t clock_index = clock_recovery.sample_index();
+ uint8_t clock_diff = std::abs(sample_index - clock_index);
+ uint8_t sync_diff = std::abs(sample_index - sync_sample_index);
+ bool clock_diff_ok = clock_diff <= 1 || clock_diff == 9;
+ bool sync_diff_ok = sync_diff <= 1 || sync_diff == 9;
+ if (clock_diff_ok) sample_index = clock_index;
+ else if (sync_diff_ok) sample_index = sync_sample_index;
+ // else unchanged.
+ need_clock_update_ = false;
+ }
}
- clock_recovery(filtered_sample);
+ clock_recovery(filtered_sample);
- if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index)
- {
- dev.sample(filtered_sample);
- }
+ if (demodState != DemodState::UNLOCKED && correlator.index() == sample_index)
+ {
+ dev.sample(filtered_sample);
+ }
switch (demodState)
{
case DemodState::UNLOCKED:
- // In this state, the sample_index is unknown. We need to find
- // a sync word to find the proper sample_index. We only leave
- // this state if we believe that we have a valid sample_index.
- do_unlocked();
- break;
+ // In this state, the sample_index is unknown. We need to find
+ // a sync word to find the proper sample_index. We only leave
+ // this state if we believe that we have a valid sample_index.
+ do_unlocked();
+ break;
case DemodState::LSF_SYNC:
- do_lsf_sync();
+ do_lsf_sync();
break;
case DemodState::STREAM_SYNC:
- do_stream_sync();
+ do_stream_sync();
break;
case DemodState::PACKET_SYNC:
- do_packet_sync();
- break;
+ do_packet_sync();
+ break;
+ case DemodState::BERT_SYNC:
+ do_bert_sync();
+ break;
case DemodState::FRAME:
- do_frame(filtered_sample,frame_result);
- break;
+ do_frame(filtered_sample,frame_result);
+ break;
}
}
dcd.update();
diff --git a/TNC/M17Demodulator.h b/TNC/M17Demodulator.h
index 70a6800..41d614c 100644
--- a/TNC/M17Demodulator.h
+++ b/TNC/M17Demodulator.h
@@ -49,7 +49,7 @@ struct M17Demodulator : IDemodulator
using audio_filter_t = FirFilter;
using sync_word_t = m17::SyncWord;
- enum class DemodState { UNLOCKED, LSF_SYNC, STREAM_SYNC, PACKET_SYNC, FRAME };
+ enum class DemodState { UNLOCKED, LSF_SYNC, STREAM_SYNC, PACKET_SYNC, BERT_SYNC, FRAME };
audio_filter_t demod_filter;
std::array demod_buffer;
@@ -59,7 +59,7 @@ struct M17Demodulator : IDemodulator
m17::Correlator correlator;
sync_word_t preamble_sync{{+3,-3,+3,-3,+3,-3,+3,-3}, 29.f};
sync_word_t lsf_sync{{+3,+3,+3,+3,-3,-3,+3,-3}, 31.f, -31.f};
- sync_word_t packet_sync{{3,-3,3,3,-3,-3,-3,-3}, 32.f};
+ sync_word_t packet_sync{{3,-3,3,3,-3,-3,-3,-3}, 31.f, -31.f};
m17::FreqDevEstimator dev;
@@ -97,6 +97,7 @@ struct M17Demodulator : IDemodulator
void do_lsf_sync();
void do_packet_sync();
void do_stream_sync();
+ void do_bert_sync();
void do_frame(float filtered_sample, hdlc::IoFrame*& frame_result);
void stop() override
@@ -139,7 +140,7 @@ struct M17Demodulator : IDemodulator
uint32_t readBatteryLevel() override
{
#ifndef NUCLEOTNC
- DEBUG("enter M17Demodulator::readBatteryLevel");
+ TNC_DEBUG("enter M17Demodulator::readBatteryLevel");
ADC_ChannelConfTypeDef sConfig;
@@ -170,21 +171,21 @@ struct M17Demodulator : IDemodulator
gpio::BAT_DIVIDER::off();
HAL_Delay(1);
- sConfig.Channel = ADC_CHANNEL_15;
- if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
+ sConfig.Channel = BATTERY_ADC_CHANNEL;
+ if (HAL_ADC_ConfigChannel(&BATTERY_ADC_HANDLE, &sConfig) != HAL_OK)
CxxErrorHandler();
uint32_t vbat = 0;
- if (HAL_ADC_Start(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Start(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
for (size_t i = 0; i != 8; ++i)
{
- if (HAL_ADC_PollForConversion(&hadc1, 1) != HAL_OK) CxxErrorHandler();
- vbat += HAL_ADC_GetValue(&hadc1);
+ if (HAL_ADC_PollForConversion(&BATTERY_ADC_HANDLE, 1) != HAL_OK) CxxErrorHandler();
+ vbat += HAL_ADC_GetValue(&BATTERY_ADC_HANDLE);
}
vbat /= 8;
- if (HAL_ADC_Stop(&hadc1) != HAL_OK) CxxErrorHandler();
+ if (HAL_ADC_Stop(&BATTERY_ADC_HANDLE) != HAL_OK) CxxErrorHandler();
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
CxxErrorHandler();
@@ -205,7 +206,7 @@ struct M17Demodulator : IDemodulator
INFO("Vref = %lumV", vref)
INFO("Vbat = %lumV", vbat);
- DEBUG("exit M17Demodulator::readBatteryLevel");
+ TNC_DEBUG("exit M17Demodulator::readBatteryLevel");
return vbat;
#else
return 0;
diff --git a/TNC/M17Encoder.cpp b/TNC/M17Encoder.cpp
index ca9852d..e77700e 100644
--- a/TNC/M17Encoder.cpp
+++ b/TNC/M17Encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015-2020 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
#include "M17Encoder.h"
@@ -29,6 +29,7 @@ static osMessageQId m17EncoderInputQueueHandle;
static osMessageQId m17EncoderOutputQueueHandle;
extern RNG_HandleTypeDef hrng;
+extern IWDG_HandleTypeDef hiwdg;
namespace mobilinkd
{
@@ -518,6 +519,39 @@ bool M17Encoder::do_csma() {
return false;
}
+tnc::hdlc::IoFrame* M17Encoder::create_bert_frame() {
+ static PRBS9 prbs;
+
+ std::array bert_frame;
+
+ uint8_t byte = 0;
+ size_t j = 0;
+ for (size_t i = 0; i != 197; ++i) {
+ byte <<= 1;
+ byte |= prbs.generate();
+ if ((i & 7) == 7) {
+ bert_frame[j] = byte;
+ j += 1;
+ byte = 0;
+ }
+ }
+ byte <<= 3;
+ bert_frame[j] = byte;
+
+ // Encoder, puncture, interleave & randomize.
+ auto encoded = conv_encode(bert_frame, 197);
+ puncture_bytes(encoded, m17_frame, P2);
+ interleaver.interleave(m17_frame);
+ randomizer(m17_frame);
+
+ auto frame = tnc::hdlc::acquire_wait();
+
+ for (auto c : m17::BERT_SYNC) frame->push_back(c);
+ for (auto c : m17_frame) frame->push_back(c);
+
+ return frame;
+}
+
/*
* The encoder task is responsible for feeding symbols to the M17 modulator.
*/
@@ -533,6 +567,8 @@ void M17Encoder::encoderTask(void const*)
osEvent evt = osMessageGet(m17EncoderInputQueueHandle, osWaitForever);
if (evt.status != osEventMessage) continue;
+ HAL_IWDG_Refresh(&hiwdg);
+
auto frame = static_cast(evt.value.p);
if (frame->size() != 48)
{
diff --git a/TNC/M17Encoder.h b/TNC/M17Encoder.h
index 9b750a1..2e95e42 100644
--- a/TNC/M17Encoder.h
+++ b/TNC/M17Encoder.h
@@ -1,4 +1,4 @@
-// Copyright 2015-2020 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
#pragma once
@@ -64,6 +64,7 @@ struct M17Encoder : public Encoder
static const std::array LSF_SYNC;
static const std::array STREAM_SYNC;
static const std::array PACKET_SYNC;
+ static const std::array BERT_SYNC;
enum class State {INACTIVE, IDLE, ACTIVE};
enum class FrameType {BASIC_PACKET, FULL_PACKET, VOICE_STREAM};
@@ -75,6 +76,8 @@ struct M17Encoder : public Encoder
void updateModulator() override;
void stop() override;
EncoderType encoder_type() const override { return EncoderType::M17; }
+ tnc::hdlc::IoFrame* create_bert_frame() override;
+
private:
@@ -123,7 +126,9 @@ private:
/**
* Convolutional encode N bytes of data, returning N*2 + 1 bytes
* of encoded data (4 zero flush bits are added). If total bits
- * are not an even byte boundary, the
+ * are not an even byte boundary, the high bits of the last byte
+ * are used.
+ *
* @param data
* @param total_bits
* @return
diff --git a/TNC/M17FrameDecoder.h b/TNC/M17FrameDecoder.h
index c7fc578..42616a5 100644
--- a/TNC/M17FrameDecoder.h
+++ b/TNC/M17FrameDecoder.h
@@ -40,6 +40,12 @@ tnc::hdlc::IoFrame* to_frame(std::array in)
}
}
+ if (b) // Not a full byte boundary.
+ {
+ while (b++ != 8) out <<= 1;
+ frame->push_back(out);
+ }
+
return frame;
}
@@ -153,8 +159,8 @@ struct M17FrameDecoder
CRC16<0x5935, 0xFFFF> crc_;
- enum class State {LSF, STREAM, BASIC_PACKET, FULL_PACKET};
- enum class SyncWordType { LSF, STREAM, PACKET, RESERVED };
+ enum class State {LSF, STREAM, BASIC_PACKET, FULL_PACKET, BERT};
+ enum class SyncWordType { LSF, STREAM, PACKET, BERT };
enum class DecodeResult { FAIL, OK, EOS, INCOMPLETE };
State state_ = State::LSF;
@@ -164,6 +170,8 @@ struct M17FrameDecoder
using lsf_conv_buffer_t = std::array;
using lsf_buffer_t = std::array;
+ using bert_buffer_t = std::array;
+
using audio_conv_buffer_t = std::array;
using audio_buffer_t = std::array;
@@ -172,18 +180,21 @@ struct M17FrameDecoder
link_setup_callback_t link_setup_callback_;
audio_callback_t audio_callback_;
+
union
{
std::array lich;
std::array lsf;
std::array packet;
std::array stream;
+ std::array bert;
} output;
union {
std::array lsf;
std::array packet;
std::array stream;
+ std::array bert;
std::array lich;
} tmp;
@@ -290,7 +301,7 @@ struct M17FrameDecoder
for (auto c : current_lsf) lsf->push_back(c);
lsf->push_back(0);
lsf->push_back(0);
- lsf->source(0x20);
+ lsf->source(tnc::hdlc::IoFrame::STREAM);
return DecodeResult::OK;
}
lsf = nullptr;
@@ -364,13 +375,13 @@ struct M17FrameDecoder
INFO("LICH crc = %04x", checksum);
if (checksum == 0)
{
- lich_segments = 0;
+ lich_segments = 0;
state_ = State::STREAM;
lsf = tnc::hdlc::acquire_wait();
for (auto c : output.lich) lsf->push_back(c);
lsf->push_back(0);
lsf->push_back(0);
- lsf->source(0x20);
+ lsf->source(tnc::hdlc::IoFrame::STREAM);
ber = 0;
dump(output.lich);
return DecodeResult::OK;
@@ -385,6 +396,18 @@ struct M17FrameDecoder
return DecodeResult::INCOMPLETE;
}
+ [[gnu::noinline]]
+ DecodeResult decode_bert(buffer_t& buffer, tnc::hdlc::IoFrame*& bert, int& ber)
+ {
+ depuncture(buffer, tmp.bert, P2);
+ ber = viterbi_.decode(tmp.bert, output.bert);
+ bert = detail::to_frame(output.bert);
+ bert->push_back(0);
+ bert->push_back(0);
+ bert->source(tnc::hdlc::IoFrame::BERT);
+ return DecodeResult::OK;
+ }
+
[[gnu::noinline]]
DecodeResult decode_stream(buffer_t& buffer, tnc::hdlc::IoFrame*& stream, int& ber)
{
@@ -410,7 +433,7 @@ struct M17FrameDecoder
}
stream->push_back(0);
stream->push_back(0);
- stream->source(0x20);
+ stream->source(tnc::hdlc::IoFrame::STREAM);
return result;
}
@@ -524,7 +547,7 @@ struct M17FrameDecoder
}
current_packet->push_back(0);
current_packet->push_back(0);
- current_packet->source(0x10);
+ current_packet->source(tnc::hdlc::IoFrame::PACKET);
packet = current_packet;
current_packet = nullptr;
packet_frame_counter = 0;
@@ -618,9 +641,9 @@ struct M17FrameDecoder
state_ = State::LSF;
}
break;
- case SyncWordType::RESERVED:
- state_ = State::LSF;
- break;
+ case SyncWordType::BERT:
+ state_ = State::BERT;
+ return decode_bert(buffer, result, ber);
}
return DecodeResult::FAIL;
diff --git a/TNC/M17Modulator.h b/TNC/M17Modulator.h
index 3ab6df5..d105ca9 100644
--- a/TNC/M17Modulator.h
+++ b/TNC/M17Modulator.h
@@ -26,7 +26,7 @@ struct M17Modulator : Modulator
// Six buffers per M17 frame, or 12 half-buffer interrupts.
static constexpr uint8_t UPSAMPLE = 10;
static constexpr uint32_t BLOCKSIZE = 4;
- static constexpr uint32_t STATE_SIZE = (m17::FILTER_TAP_NUM_9 / UPSAMPLE) + BLOCKSIZE - 1;
+ static constexpr uint32_t STATE_SIZE = (m17::FILTER_TAP_NUM_15 / UPSAMPLE) + BLOCKSIZE - 1;
static constexpr int16_t DAC_BUFFER_LEN = 80; // 8 symbols, 16 bits, 2 bytes.
static constexpr int16_t TRANSFER_LEN = DAC_BUFFER_LEN / 2; // 4 symbols, 8 bits, 1 byte.
static constexpr uint16_t VREF = 4095;
@@ -43,13 +43,14 @@ struct M17Modulator : Modulator
volatile uint16_t stop_count = 0; // Flush the RRC matched filter.
State state{State::STOPPED};
float tmp[TRANSFER_LEN];
+ bool send_tone = false;
M17Modulator(osMessageQId queue, PTT* ptt)
: dacOutputQueueHandle_(queue), ptt_(ptt)
{
arm_fir_interpolate_init_f32(
- &fir_interpolator, UPSAMPLE, m17::FILTER_TAP_NUM_9,
- (float32_t*) m17::rrc_taps_f9.data(), fir_state.data(), BLOCKSIZE);
+ &fir_interpolator, UPSAMPLE, m17::FILTER_TAP_NUM_15,
+ (float32_t*) m17::rrc_taps_f15.data(), fir_state.data(), BLOCKSIZE);
}
~M17Modulator() override {}
@@ -102,7 +103,7 @@ struct M17Modulator : Modulator
{
case State::STOPPING:
case State::STOPPED:
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_DisableLSCO();
#endif
delay_count = 0;
@@ -127,6 +128,20 @@ struct M17Modulator : Modulator
}
}
+ constexpr std::array make_1000hz_tone()
+ {
+ std::array result;
+ for (size_t i = 0; i != result.size(); ++i) {
+ result[i] = std::sin(M_PI * i * 2.0 / result.size()) * 3;
+ }
+ return result;
+ }
+
+ void tone(uint16_t) override
+ {
+ send_tone = true;
+ }
+
// DAC DMA interrupt functions.
[[gnu::noinline]]
void fill_first(uint8_t bits) override
@@ -168,7 +183,7 @@ struct M17Modulator : Modulator
case State::STOPPED:
stop_conversion();
ptt_->off();
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
osMessagePut(audioInputQueueHandle, tnc::audio::DEMODULATOR,
@@ -206,7 +221,7 @@ struct M17Modulator : Modulator
case State::STOPPED:
stop_conversion();
ptt_->off();
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
osMessagePut(audioInputQueueHandle, tnc::audio::DEMODULATOR,
@@ -218,9 +233,10 @@ struct M17Modulator : Modulator
void abort() override
{
state = State::STOPPED;
+ send_tone = false;
stop_conversion();
ptt_->off();
-#if defined(KISS_LOGGING) && !defined(NUCLEOTNC)
+#if defined(KISS_LOGGING) && defined(HAVE_LSCO)
HAL_RCCEx_EnableLSCO(RCC_LSCOSOURCE_LSE);
#endif
// Drain the queue.
@@ -295,9 +311,28 @@ private:
}
}
+ void fill_tone(int16_t* buffer)
+ {
+ static uint8_t pos = 0;
+ static const auto Hz1000 = make_1000hz_tone();
+
+ int16_t polarity = kiss::settings().tx_rev_polarity() ? -1 : 1;
+
+ for (size_t i = 0; i != TRANSFER_LEN; ++i) {
+ buffer[i] = adjust_level(Hz1000[pos++] * polarity);
+ if (pos == Hz1000.size()) pos = 0;
+ }
+ }
+
[[gnu::noinline]]
void fill(int16_t* buffer, uint8_t bits)
{
+ if (send_tone)
+ {
+ fill_tone(buffer);
+ return;
+ }
+
int16_t polarity = kiss::settings().tx_rev_polarity() ? -1 : 1;
for (size_t i = 0; i != 4; ++i)
@@ -333,6 +368,7 @@ private:
[[gnu::noinline]]
void fill_empty(int16_t* buffer)
{
+ send_tone = false;
for (size_t i = 0; i != TRANSFER_LEN; ++i)
{
buffer[i] = 2048;
diff --git a/TNC/Modulator.hpp b/TNC/Modulator.hpp
index ab7555b..183ea67 100644
--- a/TNC/Modulator.hpp
+++ b/TNC/Modulator.hpp
@@ -59,6 +59,8 @@ struct Modulator
*/
virtual void send(uint8_t symbol) = 0;
+ virtual void tone(uint16_t freq) = 0;
+
/// The next three functions are called by the DAC DMA interrupt handler.
/**
diff --git a/TNC/ModulatorTask.cpp b/TNC/ModulatorTask.cpp
index 6dfd050..87aab2d 100644
--- a/TNC/ModulatorTask.cpp
+++ b/TNC/ModulatorTask.cpp
@@ -1,4 +1,4 @@
-// Copyright 2015 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
#include "ModulatorTask.hpp"
@@ -10,6 +10,7 @@
#include "AFSKModulator.hpp"
#include "KissHardware.hpp"
#include "main.h"
+#include "Log.h"
mobilinkd::tnc::SimplexPTT simplexPtt;
mobilinkd::tnc::MultiplexPTT multiplexPtt;
@@ -77,6 +78,7 @@ mobilinkd::Encoder& getEncoder()
case kiss::Hardware::ModemType::M17:
return m17Encoder;
default:
+ ERROR("Invalid modem type %d", int(kiss::settings().modem_type));
CxxErrorHandler();
}
}
diff --git a/TNC/NullPort.hpp b/TNC/NullPort.hpp
index 48941b6..0a1830d 100644
--- a/TNC/NullPort.hpp
+++ b/TNC/NullPort.hpp
@@ -1,8 +1,7 @@
-// Copyright 2016 Rob Riggs
+// Copyright 2016-2021 Rob Riggs
// All rights reserved.
-#ifndef MOBILINKD__TNC__NULL_PORT_HPP_
-#define MOBILINKD__TNC__NULL_PORT_HPP_
+#pragma once
#include "PortInterface.hpp"
@@ -52,5 +51,3 @@ struct NullPort : PortInterface
NullPort* getNullPort();
}} // mobilinkd::tnc
-
-#endif // MOBILINKD__TNC__NULL_PORT_HPP_
diff --git a/TNC/SegmentedBuffer.hpp b/TNC/SegmentedBuffer.hpp
index 3e0620c..66e86ec 100644
--- a/TNC/SegmentedBuffer.hpp
+++ b/TNC/SegmentedBuffer.hpp
@@ -1,8 +1,7 @@
-// Copyright 2015 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
-#ifndef MOBILINKD__SEGMENTED_BUFFER_HPP_
-#define MOBILINKD__SEGMENTED_BUFFER_HPP_
+#pragma once
#include "memory.hpp"
@@ -157,5 +156,3 @@ struct SegmentedBufferIterator : public boost::iterator_facade<
};
}}} // mobilinkd::tnc::buffer
-
-#endif // MOBILINKD__SEGMENTED_BUFFER_HPP_
diff --git a/TNC/SerialPort.cpp b/TNC/SerialPort.cpp
index b668459..3e185cc 100644
--- a/TNC/SerialPort.cpp
+++ b/TNC/SerialPort.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 Rob Riggs
+// Copyright 2016-2021 Rob Riggs
// All rights reserved.
#ifndef NUCLEOTNC
@@ -52,11 +52,11 @@ void log_frame(mobilinkd::tnc::hdlc::IoFrame* frame)
if (isprint(int(c))) pos += sprintf((char*)tmpBuffer2 + pos, " %c ", c);
else pos += sprintf((char*)tmpBuffer2 + pos, "/%02x", c);
if (pos > 80) {
- DEBUG((char*)tmpBuffer2);
+ TNC_DEBUG((char*)tmpBuffer2);
pos = 0;
}
}
- DEBUG((char*)tmpBuffer2);
+ TNC_DEBUG((char*)tmpBuffer2);
}
#endif
@@ -569,7 +569,7 @@ bool SerialPort::write(hdlc::IoFrame* frame, uint32_t timeout)
osMutexRelease(mutex_);
hdlc::release(frame);
- DEBUG("SerialPort::write COMPLETE");
+ TNC_DEBUG("SerialPort::write COMPLETE");
return true;
}
diff --git a/TNC/SerialPort.hpp b/TNC/SerialPort.hpp
index 5374c53..7dcbded 100644
--- a/TNC/SerialPort.hpp
+++ b/TNC/SerialPort.hpp
@@ -1,8 +1,7 @@
-// Copyright 2016 Rob Riggs
+// Copyright 2016-2021 Rob Riggs
// All rights reserved.
-#ifndef MOBILINKD__TNC__SERIAL_PORT_HPP_
-#define MOBILINKD__TNC__SERIAL_PORT_HPP_
+#pragma once
#include "PortInterface.hpp"
@@ -38,5 +37,3 @@ private:
SerialPort* getSerialPort();
}} // mobilinkd::tnc
-
-#endif // MOBILINKD__TNC__SERIAL_PORT_HPP_
diff --git a/TNC/Util.h b/TNC/Util.h
index 50379cd..21a1baa 100644
--- a/TNC/Util.h
+++ b/TNC/Util.h
@@ -1,4 +1,4 @@
-// Copyright 2020 Mobilinkd LLC.
+// Copyright 2020-2021 Mobilinkd LLC.
#pragma once
@@ -314,4 +314,85 @@ constexpr void to_byte_array(std::array in, std::array history;
+ size_t hist_count = 0;
+ size_t hist_pos = 0;
+
+ // PRBS generator.
+ bool generate()
+ {
+ bool result = ((state >> TAP_1) ^ (state >> TAP_2)) & 1;
+ state = ((state << 1) | result) & MASK;
+ return result;
+ }
+
+ // PRBS validator.
+ bool validate(bool bit)
+ {
+ bool result;
+ bit_count += 1;
+ if (!synced) {
+ // Need to sync the PRBS with the incoming data.
+ result = (bit ^ (state >> TAP_1) ^ (state >> TAP_2)) & 1;
+ state = ((state << 1) | bit) & MASK;
+ if (result) {
+ err_count += 1;
+ sync_count = 0; // error
+ } else {
+ if (++sync_count == LOCK_COUNT) {
+ synced = true;
+ history.fill(0);
+ hist_count = 0;
+ hist_pos = 0;
+ sync_count = 0;
+ }
+ }
+ } else {
+ // PRBS is now free-running.
+ result = generate();
+
+ hist_count -= (history[hist_pos >> 3] & (1 << (hist_pos & 7))) != 0;
+ if (result != bit) {
+ err_count += 1;
+ hist_count += 1;
+ history[hist_pos >> 3] |= (1 << (hist_pos & 7));
+ } else {
+ history[hist_pos >> 3] &= ~(1 << (hist_pos & 7));
+ }
+ if (++hist_pos == 128) hist_pos = 0;
+ }
+ return result;
+ }
+
+ bool sync() const { return synced; }
+
+ uint32_t errors() const { assert(synced); return err_count; }
+ uint32_t bits() const { assert(synced); return bit_count; }
+
+ // Reset the state.
+ void reset()
+ {
+ state = 1;
+ synced = false;
+ sync_count = 0;
+ bit_count = 0;
+ err_count = 0;
+ history.fill(0);
+ hist_count = 0;
+ hist_pos = 0;
+ }
+};
+
} // mobilinkd
diff --git a/TNC/memory.hpp b/TNC/memory.hpp
index e3ae05f..ae1789a 100644
--- a/TNC/memory.hpp
+++ b/TNC/memory.hpp
@@ -1,8 +1,7 @@
-// Copyright 2015 Mobilinkd LLC
+// Copyright 2015-2021 Mobilinkd LLC
// All rights reserved.
-#ifndef MOBILINKD__MEMORY_HPP_
-#define MOBILINKD__MEMORY_HPP_
+#pragma once
#include "cmsis_os.h"
@@ -66,9 +65,9 @@ struct Pool {
free_list.push_back(*item);
taskEXIT_CRITICAL_FROM_ISR(x);
}
+
+ size_t free() const { return free_list.size(); }
};
}}} // mobilinkd::tnc::memory
-
-#endif // MOBILINKD__MEMORY_HPP_