kopia lustrzana https://github.com/mobilinkd/NucleoTNC
Massive update to get the NucleoTNC firmware up to date with the TNC3. Mostly improvements to the moduleator and demodulator sections. Fix EEPROM read/write. Update version number to 1.0.0.
rodzic
1d228b0413
commit
777919da49
12
Inc/main.h
12
Inc/main.h
|
@ -58,7 +58,6 @@
|
|||
/* USER CODE END Includes */
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
|
||||
#define VCP_TX_Pin GPIO_PIN_2
|
||||
#define VCP_TX_GPIO_Port GPIOA
|
||||
#define AUDIO_IN_Pin GPIO_PIN_3
|
||||
|
@ -100,17 +99,26 @@
|
|||
|
||||
/* USER CODE BEGIN Private defines */
|
||||
|
||||
#define EEPROM_ADDRESS 0xA0
|
||||
#define EEPROM_CAPACITY 4096
|
||||
#define EEPROM_PAGE_SIZE 32
|
||||
#define EEPROM_WRITE_TIME 5
|
||||
|
||||
#define CMD_USER_BUTTON_DOWN 1
|
||||
#define CMD_USER_BUTTON_UP 2
|
||||
#define CMD_SET_PTT_SIMPLEX 3
|
||||
#define CMD_SET_PTT_MULTIPLEX 4
|
||||
|
||||
#define CxxErrorHandler() _Error_Handler(const_cast<char*>(__FILE__), __LINE__)
|
||||
|
||||
extern char serial_number_64[17];
|
||||
|
||||
/* USER CODE END Private defines */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void _Error_Handler(char *, int);
|
||||
void _Error_Handler(const char *, int);
|
||||
|
||||
#define Error_Handler() _Error_Handler(__FILE__, __LINE__)
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -39,15 +39,15 @@ CRC.InputDataInversionMode=CRC_INPUTDATA_INVERSION_BYTE
|
|||
DAC1.DAC_Channel-DAC_OUT2=DAC_CHANNEL_2
|
||||
DAC1.DAC_Trigger-DAC_OUT1=DAC_TRIGGER_T7_TRGO
|
||||
DAC1.IPParameters=DAC_Channel-DAC_OUT2,DAC_Trigger-DAC_OUT1
|
||||
Dma.ADC1.5.Direction=DMA_PERIPH_TO_MEMORY
|
||||
Dma.ADC1.5.Instance=DMA1_Channel1
|
||||
Dma.ADC1.5.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
|
||||
Dma.ADC1.5.MemInc=DMA_MINC_ENABLE
|
||||
Dma.ADC1.5.Mode=DMA_CIRCULAR
|
||||
Dma.ADC1.5.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
|
||||
Dma.ADC1.5.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.ADC1.5.Priority=DMA_PRIORITY_LOW
|
||||
Dma.ADC1.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.ADC1.3.Direction=DMA_PERIPH_TO_MEMORY
|
||||
Dma.ADC1.3.Instance=DMA1_Channel1
|
||||
Dma.ADC1.3.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
|
||||
Dma.ADC1.3.MemInc=DMA_MINC_ENABLE
|
||||
Dma.ADC1.3.Mode=DMA_CIRCULAR
|
||||
Dma.ADC1.3.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
|
||||
Dma.ADC1.3.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.ADC1.3.Priority=DMA_PRIORITY_LOW
|
||||
Dma.ADC1.3.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.DAC_CH1.2.Direction=DMA_MEMORY_TO_PERIPH
|
||||
Dma.DAC_CH1.2.Instance=DMA2_Channel4
|
||||
Dma.DAC_CH1.2.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
|
||||
|
@ -57,30 +57,30 @@ Dma.DAC_CH1.2.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
|
|||
Dma.DAC_CH1.2.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.DAC_CH1.2.Priority=DMA_PRIORITY_LOW
|
||||
Dma.DAC_CH1.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.I2C3_RX.3.Direction=DMA_PERIPH_TO_MEMORY
|
||||
Dma.I2C3_RX.3.Instance=DMA1_Channel3
|
||||
Dma.I2C3_RX.3.MemDataAlignment=DMA_MDATAALIGN_BYTE
|
||||
Dma.I2C3_RX.3.MemInc=DMA_MINC_ENABLE
|
||||
Dma.I2C3_RX.3.Mode=DMA_NORMAL
|
||||
Dma.I2C3_RX.3.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
|
||||
Dma.I2C3_RX.3.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.I2C3_RX.3.Priority=DMA_PRIORITY_LOW
|
||||
Dma.I2C3_RX.3.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.I2C3_TX.4.Direction=DMA_MEMORY_TO_PERIPH
|
||||
Dma.I2C3_TX.4.Instance=DMA1_Channel2
|
||||
Dma.I2C3_TX.4.MemDataAlignment=DMA_MDATAALIGN_BYTE
|
||||
Dma.I2C3_TX.4.MemInc=DMA_MINC_ENABLE
|
||||
Dma.I2C3_TX.4.Mode=DMA_NORMAL
|
||||
Dma.I2C3_TX.4.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
|
||||
Dma.I2C3_TX.4.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.I2C3_TX.4.Priority=DMA_PRIORITY_LOW
|
||||
Dma.I2C3_TX.4.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.I2C3_RX.4.Direction=DMA_PERIPH_TO_MEMORY
|
||||
Dma.I2C3_RX.4.Instance=DMA1_Channel3
|
||||
Dma.I2C3_RX.4.MemDataAlignment=DMA_MDATAALIGN_BYTE
|
||||
Dma.I2C3_RX.4.MemInc=DMA_MINC_ENABLE
|
||||
Dma.I2C3_RX.4.Mode=DMA_NORMAL
|
||||
Dma.I2C3_RX.4.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
|
||||
Dma.I2C3_RX.4.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.I2C3_RX.4.Priority=DMA_PRIORITY_LOW
|
||||
Dma.I2C3_RX.4.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.I2C3_TX.5.Direction=DMA_MEMORY_TO_PERIPH
|
||||
Dma.I2C3_TX.5.Instance=DMA1_Channel2
|
||||
Dma.I2C3_TX.5.MemDataAlignment=DMA_MDATAALIGN_BYTE
|
||||
Dma.I2C3_TX.5.MemInc=DMA_MINC_ENABLE
|
||||
Dma.I2C3_TX.5.Mode=DMA_NORMAL
|
||||
Dma.I2C3_TX.5.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
|
||||
Dma.I2C3_TX.5.PeriphInc=DMA_PINC_DISABLE
|
||||
Dma.I2C3_TX.5.Priority=DMA_PRIORITY_LOW
|
||||
Dma.I2C3_TX.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority
|
||||
Dma.Request0=USART2_RX
|
||||
Dma.Request1=USART2_TX
|
||||
Dma.Request2=DAC_CH1
|
||||
Dma.Request3=I2C3_RX
|
||||
Dma.Request4=I2C3_TX
|
||||
Dma.Request5=ADC1
|
||||
Dma.Request3=ADC1
|
||||
Dma.Request4=I2C3_RX
|
||||
Dma.Request5=I2C3_TX
|
||||
Dma.RequestsNb=6
|
||||
Dma.USART2_RX.0.Direction=DMA_PERIPH_TO_MEMORY
|
||||
Dma.USART2_RX.0.Instance=DMA1_Channel6
|
||||
|
@ -112,9 +112,9 @@ FREERTOS.configTOTAL_HEAP_SIZE=4096
|
|||
FREERTOS.configUSE_TICKLESS_IDLE=1
|
||||
FREERTOS.configUSE_TIMERS=1
|
||||
File.Version=6
|
||||
I2C3.I2C_Speed_Mode=I2C_Fast_Plus
|
||||
I2C3.I2C_Speed_Mode=I2C_Fast
|
||||
I2C3.IPParameters=Timing,I2C_Speed_Mode
|
||||
I2C3.Timing=0x20000209
|
||||
I2C3.Timing=0x2010091A
|
||||
KeepUserPlacement=true
|
||||
Mcu.Family=STM32L4
|
||||
Mcu.IP0=ADC1
|
||||
|
@ -169,8 +169,8 @@ Mcu.PinsNb=31
|
|||
Mcu.ThirdPartyNb=0
|
||||
Mcu.UserConstants=
|
||||
Mcu.UserName=STM32L432KCUx
|
||||
MxCube.Version=4.26.1
|
||||
MxDb.Version=DB.4.0.261
|
||||
MxCube.Version=4.27.0
|
||||
MxDb.Version=DB.4.0.270
|
||||
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:false\:false\:false
|
||||
NVIC.DMA1_Channel1_IRQn=true\:5\:0\:false\:false\:true\:true\:false
|
||||
NVIC.DMA1_Channel2_IRQn=true\:5\:0\:false\:false\:true\:true\:false
|
||||
|
@ -320,6 +320,7 @@ ProjectManager.KeepUserCode=true
|
|||
ProjectManager.LastFirmware=true
|
||||
ProjectManager.LibraryCopy=1
|
||||
ProjectManager.MainLocation=Src
|
||||
ProjectManager.NoMain=false
|
||||
ProjectManager.PreviousToolchain=TrueSTUDIO
|
||||
ProjectManager.ProjectBuild=false
|
||||
ProjectManager.ProjectFileName=Nucleo_L432KC_TNC.ioc
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/* ----------------------------------------------------------------------
|
||||
* Copyright (C) 2010-2014 ARM Limited. All rights reserved.
|
||||
*
|
||||
* $Date: 19. March 2015
|
||||
* $Revision: V.1.4.5
|
||||
*
|
||||
* Project: CMSIS DSP Library
|
||||
* Title: arm_fir_fast_q15.c
|
||||
*
|
||||
* Description: Q15 Fast FIR filter processing function.
|
||||
*
|
||||
* Target Processor: Cortex-M4/Cortex-M3
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
* - Neither the name of ARM LIMITED 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 OWNER 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.
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
#include "arm_math.h"
|
||||
|
||||
/**
|
||||
* @ingroup groupFilters
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup FIR
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param[in] *S points to an instance of the Q15 FIR filter structure.
|
||||
* @param[in] *pSrc points to the block of input data.
|
||||
* @param[out] *pDst points to the block of output data.
|
||||
* @param[in] blockSize number of samples to process per call.
|
||||
* @return none.
|
||||
*
|
||||
* <b>Scaling and Overflow Behavior:</b>
|
||||
* \par
|
||||
* This fast version uses a 32-bit accumulator with 2.30 format.
|
||||
* The accumulator maintains full precision of the intermediate multiplication results but provides only a single guard bit.
|
||||
* Thus, if the accumulator result overflows it wraps around and distorts the result.
|
||||
* In order to avoid overflows completely the input signal must be scaled down by log2(numTaps) bits.
|
||||
* The 2.30 accumulator is then truncated to 2.15 format and saturated to yield the 1.15 result.
|
||||
*
|
||||
* \par
|
||||
* Refer to the function <code>arm_fir_q15()</code> for a slower implementation of this function which uses 64-bit accumulation to avoid wrap around distortion. Both the slow and the fast versions use the same instance structure.
|
||||
* Use the function <code>arm_fir_init_q15()</code> to initialize the filter structure.
|
||||
*/
|
||||
|
||||
void arm_fir_fast_q15(
|
||||
const arm_fir_instance_q15 * S,
|
||||
q15_t * pSrc,
|
||||
q15_t * pDst,
|
||||
uint32_t blockSize)
|
||||
{
|
||||
q15_t *pState = S->pState; /* State pointer */
|
||||
q15_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
|
||||
q15_t *pStateCurnt; /* Points to the current sample of the state */
|
||||
q31_t acc0, acc1, acc2, acc3; /* Accumulators */
|
||||
q15_t *pb; /* Temporary pointer for coefficient buffer */
|
||||
q15_t *px; /* Temporary q31 pointer for SIMD state buffer accesses */
|
||||
q31_t x0, x1, x2, c0; /* Temporary variables to hold SIMD state and coefficient values */
|
||||
uint32_t numTaps = S->numTaps; /* Number of taps in the filter */
|
||||
uint32_t tapCnt, blkCnt; /* Loop counters */
|
||||
|
||||
|
||||
/* S->pState points to state array which contains previous frame (numTaps - 1) samples */
|
||||
/* pStateCurnt points to the location where the new input data should be written */
|
||||
pStateCurnt = &(S->pState[(numTaps - 1u)]);
|
||||
|
||||
/* Apply loop unrolling and compute 4 output values simultaneously.
|
||||
* The variables acc0 ... acc3 hold output values that are being computed:
|
||||
*
|
||||
* acc0 = b[numTaps-1] * x[n-numTaps-1] + b[numTaps-2] * x[n-numTaps-2] + b[numTaps-3] * x[n-numTaps-3] +...+ b[0] * x[0]
|
||||
* acc1 = b[numTaps-1] * x[n-numTaps] + b[numTaps-2] * x[n-numTaps-1] + b[numTaps-3] * x[n-numTaps-2] +...+ b[0] * x[1]
|
||||
* acc2 = b[numTaps-1] * x[n-numTaps+1] + b[numTaps-2] * x[n-numTaps] + b[numTaps-3] * x[n-numTaps-1] +...+ b[0] * x[2]
|
||||
* acc3 = b[numTaps-1] * x[n-numTaps+2] + b[numTaps-2] * x[n-numTaps+1] + b[numTaps-3] * x[n-numTaps] +...+ b[0] * x[3]
|
||||
*/
|
||||
|
||||
blkCnt = blockSize >> 2;
|
||||
|
||||
/* First part of the processing with loop unrolling. Compute 4 outputs at a time.
|
||||
** a second loop below computes the remaining 1 to 3 samples. */
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* Copy four new input samples into the state buffer.
|
||||
** Use 32-bit SIMD to move the 16-bit data. Only requires two copies. */
|
||||
*pStateCurnt++ = *pSrc++;
|
||||
*pStateCurnt++ = *pSrc++;
|
||||
*pStateCurnt++ = *pSrc++;
|
||||
*pStateCurnt++ = *pSrc++;
|
||||
|
||||
|
||||
/* Set all accumulators to zero */
|
||||
acc0 = 0;
|
||||
acc1 = 0;
|
||||
acc2 = 0;
|
||||
acc3 = 0;
|
||||
|
||||
/* Typecast q15_t pointer to q31_t pointer for state reading in q31_t */
|
||||
px = pState;
|
||||
|
||||
/* Typecast q15_t pointer to q31_t pointer for coefficient reading in q31_t */
|
||||
pb = pCoeffs;
|
||||
|
||||
/* Read the first two samples from the state buffer: x[n-N], x[n-N-1] */
|
||||
x0 = *__SIMD32(px)++;
|
||||
|
||||
/* Read the third and forth samples from the state buffer: x[n-N-2], x[n-N-3] */
|
||||
x2 = *__SIMD32(px)++;
|
||||
|
||||
/* Loop over the number of taps. Unroll by a factor of 4.
|
||||
** Repeat until we've computed numTaps-(numTaps%4) coefficients. */
|
||||
tapCnt = numTaps >> 2;
|
||||
|
||||
while(tapCnt > 0)
|
||||
{
|
||||
/* Read the first two coefficients using SIMD: b[N] and b[N-1] coefficients */
|
||||
c0 = *__SIMD32(pb)++;
|
||||
|
||||
/* acc0 += b[N] * x[n-N] + b[N-1] * x[n-N-1] */
|
||||
acc0 = __SMLAD(x0, c0, acc0);
|
||||
|
||||
/* acc2 += b[N] * x[n-N-2] + b[N-1] * x[n-N-3] */
|
||||
acc2 = __SMLAD(x2, c0, acc2);
|
||||
|
||||
/* pack x[n-N-1] and x[n-N-2] */
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
x1 = __PKHBT(x2, x0, 0);
|
||||
#else
|
||||
x1 = __PKHBT(x0, x2, 0);
|
||||
#endif
|
||||
|
||||
/* Read state x[n-N-4], x[n-N-5] */
|
||||
x0 = _SIMD32_OFFSET(px);
|
||||
|
||||
/* acc1 += b[N] * x[n-N-1] + b[N-1] * x[n-N-2] */
|
||||
acc1 = __SMLADX(x1, c0, acc1);
|
||||
|
||||
/* pack x[n-N-3] and x[n-N-4] */
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
x1 = __PKHBT(x0, x2, 0);
|
||||
#else
|
||||
x1 = __PKHBT(x2, x0, 0);
|
||||
#endif
|
||||
|
||||
/* acc3 += b[N] * x[n-N-3] + b[N-1] * x[n-N-4] */
|
||||
acc3 = __SMLADX(x1, c0, acc3);
|
||||
|
||||
/* Read coefficients b[N-2], b[N-3] */
|
||||
c0 = *__SIMD32(pb)++;
|
||||
|
||||
/* acc0 += b[N-2] * x[n-N-2] + b[N-3] * x[n-N-3] */
|
||||
acc0 = __SMLAD(x2, c0, acc0);
|
||||
|
||||
/* Read state x[n-N-6], x[n-N-7] with offset */
|
||||
x2 = _SIMD32_OFFSET(px + 2u);
|
||||
|
||||
/* acc2 += b[N-2] * x[n-N-4] + b[N-3] * x[n-N-5] */
|
||||
acc2 = __SMLAD(x0, c0, acc2);
|
||||
|
||||
/* acc1 += b[N-2] * x[n-N-3] + b[N-3] * x[n-N-4] */
|
||||
acc1 = __SMLADX(x1, c0, acc1);
|
||||
|
||||
/* pack x[n-N-5] and x[n-N-6] */
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
x1 = __PKHBT(x2, x0, 0);
|
||||
#else
|
||||
x1 = __PKHBT(x0, x2, 0);
|
||||
#endif
|
||||
|
||||
/* acc3 += b[N-2] * x[n-N-5] + b[N-3] * x[n-N-6] */
|
||||
acc3 = __SMLADX(x1, c0, acc3);
|
||||
|
||||
/* Update state pointer for next state reading */
|
||||
px += 4u;
|
||||
|
||||
/* Decrement tap count */
|
||||
tapCnt--;
|
||||
|
||||
}
|
||||
|
||||
/* If the filter length is not a multiple of 4, compute the remaining filter taps.
|
||||
** This is always be 2 taps since the filter length is even. */
|
||||
if((numTaps & 0x3u) != 0u)
|
||||
{
|
||||
|
||||
/* Read last two coefficients */
|
||||
c0 = *__SIMD32(pb)++;
|
||||
|
||||
/* Perform the multiply-accumulates */
|
||||
acc0 = __SMLAD(x0, c0, acc0);
|
||||
acc2 = __SMLAD(x2, c0, acc2);
|
||||
|
||||
/* pack state variables */
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
x1 = __PKHBT(x2, x0, 0);
|
||||
#else
|
||||
x1 = __PKHBT(x0, x2, 0);
|
||||
#endif
|
||||
|
||||
/* Read last state variables */
|
||||
x0 = *__SIMD32(px);
|
||||
|
||||
/* Perform the multiply-accumulates */
|
||||
acc1 = __SMLADX(x1, c0, acc1);
|
||||
|
||||
/* pack state variables */
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
x1 = __PKHBT(x0, x2, 0);
|
||||
#else
|
||||
x1 = __PKHBT(x2, x0, 0);
|
||||
#endif
|
||||
|
||||
/* Perform the multiply-accumulates */
|
||||
acc3 = __SMLADX(x1, c0, acc3);
|
||||
}
|
||||
|
||||
/* The results in the 4 accumulators are in 2.30 format. Convert to 1.15 with saturation.
|
||||
** Then store the 4 outputs in the destination buffer. */
|
||||
|
||||
#ifndef ARM_MATH_BIG_ENDIAN
|
||||
|
||||
*__SIMD32(pDst)++ =
|
||||
__PKHBT(__SSAT((acc0 >> 15), 16), __SSAT((acc1 >> 15), 16), 16);
|
||||
|
||||
*__SIMD32(pDst)++ =
|
||||
__PKHBT(__SSAT((acc2 >> 15), 16), __SSAT((acc3 >> 15), 16), 16);
|
||||
|
||||
#else
|
||||
|
||||
*__SIMD32(pDst)++ =
|
||||
__PKHBT(__SSAT((acc1 >> 15), 16), __SSAT((acc0 >> 15), 16), 16);
|
||||
|
||||
*__SIMD32(pDst)++ =
|
||||
__PKHBT(__SSAT((acc3 >> 15), 16), __SSAT((acc2 >> 15), 16), 16);
|
||||
|
||||
|
||||
#endif /* #ifndef ARM_MATH_BIG_ENDIAN */
|
||||
|
||||
/* Advance the state pointer by 4 to process the next group of 4 samples */
|
||||
pState = pState + 4u;
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
/* If the blockSize is not a multiple of 4, compute any remaining output samples here.
|
||||
** No loop unrolling is used. */
|
||||
blkCnt = blockSize % 0x4u;
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* Copy two samples into state buffer */
|
||||
*pStateCurnt++ = *pSrc++;
|
||||
|
||||
/* Set the accumulator to zero */
|
||||
acc0 = 0;
|
||||
|
||||
/* Use SIMD to hold states and coefficients */
|
||||
px = pState;
|
||||
pb = pCoeffs;
|
||||
|
||||
tapCnt = numTaps >> 1u;
|
||||
|
||||
do
|
||||
{
|
||||
|
||||
acc0 += (q31_t) * px++ * *pb++;
|
||||
acc0 += (q31_t) * px++ * *pb++;
|
||||
|
||||
tapCnt--;
|
||||
}
|
||||
while(tapCnt > 0u);
|
||||
|
||||
/* The result is in 2.30 format. Convert to 1.15 with saturation.
|
||||
** Then store the output in the destination buffer. */
|
||||
*pDst++ = (q15_t) (__SSAT((acc0 >> 15), 16));
|
||||
|
||||
/* Advance state pointer by 1 for the next sample */
|
||||
pState = pState + 1u;
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
/* Processing is complete.
|
||||
** Now copy the last numTaps - 1 samples to the satrt of the state buffer.
|
||||
** This prepares the state buffer for the next function call. */
|
||||
|
||||
/* Points to the start of the state buffer */
|
||||
pStateCurnt = S->pState;
|
||||
|
||||
/* Calculation of count for copying integer writes */
|
||||
tapCnt = (numTaps - 1u) >> 2;
|
||||
|
||||
while(tapCnt > 0u)
|
||||
{
|
||||
*pStateCurnt++ = *pState++;
|
||||
*pStateCurnt++ = *pState++;
|
||||
*pStateCurnt++ = *pState++;
|
||||
*pStateCurnt++ = *pState++;
|
||||
|
||||
tapCnt--;
|
||||
|
||||
}
|
||||
|
||||
/* Calculation of count for remaining q15_t data */
|
||||
tapCnt = (numTaps - 1u) % 0x4u;
|
||||
|
||||
/* copy remaining data */
|
||||
while(tapCnt > 0u)
|
||||
{
|
||||
*pStateCurnt++ = *pState++;
|
||||
|
||||
/* Decrement the loop counter */
|
||||
tapCnt--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @} end of FIR group
|
||||
*/
|
|
@ -0,0 +1,154 @@
|
|||
/* ----------------------------------------------------------------------
|
||||
* Copyright (C) 2010-2014 ARM Limited. All rights reserved.
|
||||
*
|
||||
* $Date: 19. March 2015
|
||||
* $Revision: V.1.4.5
|
||||
*
|
||||
* Project: CMSIS DSP Library
|
||||
* Title: arm_fir_init_q15.c
|
||||
*
|
||||
* Description: Q15 FIR filter initialization function.
|
||||
*
|
||||
* Target Processor: Cortex-M4/Cortex-M3/Cortex-M0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
* - Neither the name of ARM LIMITED 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 OWNER 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.
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
#include "arm_math.h"
|
||||
|
||||
/**
|
||||
* @ingroup groupFilters
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup FIR
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param[in,out] *S points to an instance of the Q15 FIR filter structure.
|
||||
* @param[in] numTaps Number of filter coefficients in the filter. Must be even and greater than or equal to 4.
|
||||
* @param[in] *pCoeffs points to the filter coefficients buffer.
|
||||
* @param[in] *pState points to the state buffer.
|
||||
* @param[in] blockSize is number of samples processed per call.
|
||||
* @return The function returns ARM_MATH_SUCCESS if initialization is successful or ARM_MATH_ARGUMENT_ERROR if
|
||||
* <code>numTaps</code> is not greater than or equal to 4 and even.
|
||||
*
|
||||
* <b>Description:</b>
|
||||
* \par
|
||||
* <code>pCoeffs</code> points to the array of filter coefficients stored in time reversed order:
|
||||
* <pre>
|
||||
* {b[numTaps-1], b[numTaps-2], b[N-2], ..., b[1], b[0]}
|
||||
* </pre>
|
||||
* Note that <code>numTaps</code> must be even and greater than or equal to 4.
|
||||
* To implement an odd length filter simply increase <code>numTaps</code> by 1 and set the last coefficient to zero.
|
||||
* For example, to implement a filter with <code>numTaps=3</code> and coefficients
|
||||
* <pre>
|
||||
* {0.3, -0.8, 0.3}
|
||||
* </pre>
|
||||
* set <code>numTaps=4</code> and use the coefficients:
|
||||
* <pre>
|
||||
* {0.3, -0.8, 0.3, 0}.
|
||||
* </pre>
|
||||
* Similarly, to implement a two point filter
|
||||
* <pre>
|
||||
* {0.3, -0.3}
|
||||
* </pre>
|
||||
* set <code>numTaps=4</code> and use the coefficients:
|
||||
* <pre>
|
||||
* {0.3, -0.3, 0, 0}.
|
||||
* </pre>
|
||||
* \par
|
||||
* <code>pState</code> points to the array of state variables.
|
||||
* <code>pState</code> is of length <code>numTaps+blockSize</code>, when running on Cortex-M4 and Cortex-M3 and is of length <code>numTaps+blockSize-1</code>, when running on Cortex-M0 where <code>blockSize</code> is the number of input samples processed by each call to <code>arm_fir_q15()</code>.
|
||||
*/
|
||||
|
||||
arm_status arm_fir_init_q15(
|
||||
arm_fir_instance_q15 * S,
|
||||
uint16_t numTaps,
|
||||
q15_t * pCoeffs,
|
||||
q15_t * pState,
|
||||
uint32_t blockSize)
|
||||
{
|
||||
arm_status status;
|
||||
|
||||
|
||||
#ifndef ARM_MATH_CM0_FAMILY
|
||||
|
||||
/* Run the below code for Cortex-M4 and Cortex-M3 */
|
||||
|
||||
/* The Number of filter coefficients in the filter must be even and at least 4 */
|
||||
if(numTaps & 0x1u)
|
||||
{
|
||||
status = ARM_MATH_ARGUMENT_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Assign filter taps */
|
||||
S->numTaps = numTaps;
|
||||
|
||||
/* Assign coefficient pointer */
|
||||
S->pCoeffs = pCoeffs;
|
||||
|
||||
/* Clear the state buffer. The size is always (blockSize + numTaps ) */
|
||||
memset(pState, 0, (numTaps + (blockSize)) * sizeof(q15_t));
|
||||
|
||||
/* Assign state pointer */
|
||||
S->pState = pState;
|
||||
|
||||
status = ARM_MATH_SUCCESS;
|
||||
}
|
||||
|
||||
return (status);
|
||||
|
||||
#else
|
||||
|
||||
/* Run the below code for Cortex-M0 */
|
||||
|
||||
/* Assign filter taps */
|
||||
S->numTaps = numTaps;
|
||||
|
||||
/* Assign coefficient pointer */
|
||||
S->pCoeffs = pCoeffs;
|
||||
|
||||
/* Clear the state buffer. The size is always (blockSize + numTaps - 1) */
|
||||
memset(pState, 0, (numTaps + (blockSize - 1u)) * sizeof(q15_t));
|
||||
|
||||
/* Assign state pointer */
|
||||
S->pState = pState;
|
||||
|
||||
status = ARM_MATH_SUCCESS;
|
||||
|
||||
return (status);
|
||||
|
||||
#endif /* #ifndef ARM_MATH_CM0_FAMILY */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @} end of FIR group
|
||||
*/
|
|
@ -0,0 +1,136 @@
|
|||
/* ----------------------------------------------------------------------
|
||||
* Copyright (C) 2010-2014 ARM Limited. All rights reserved.
|
||||
*
|
||||
* $Date: 19. March 2015
|
||||
* $Revision: V.1.4.5
|
||||
*
|
||||
* Project: CMSIS DSP Library
|
||||
* Title: arm_offset_q15.c
|
||||
*
|
||||
* Description: Q15 vector offset.
|
||||
*
|
||||
* Target Processor: Cortex-M4/Cortex-M3/Cortex-M0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
* - Neither the name of ARM LIMITED 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 OWNER 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.
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
#include "arm_math.h"
|
||||
|
||||
/**
|
||||
* @ingroup groupMath
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup offset
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Adds a constant offset to a Q15 vector.
|
||||
* @param[in] *pSrc points to the input vector
|
||||
* @param[in] offset is the offset to be added
|
||||
* @param[out] *pDst points to the output vector
|
||||
* @param[in] blockSize number of samples in the vector
|
||||
* @return none.
|
||||
*
|
||||
* <b>Scaling and Overflow Behavior:</b>
|
||||
* \par
|
||||
* The function uses saturating arithmetic.
|
||||
* Results outside of the allowable Q15 range [0x8000 0x7FFF] are saturated.
|
||||
*/
|
||||
|
||||
void arm_offset_q15(
|
||||
q15_t * pSrc,
|
||||
q15_t offset,
|
||||
q15_t * pDst,
|
||||
uint32_t blockSize)
|
||||
{
|
||||
uint32_t blkCnt; /* loop counter */
|
||||
|
||||
#ifndef ARM_MATH_CM0_FAMILY
|
||||
|
||||
/* Run the below code for Cortex-M4 and Cortex-M3 */
|
||||
q31_t offset_packed; /* Offset packed to 32 bit */
|
||||
|
||||
|
||||
/*loop Unrolling */
|
||||
blkCnt = blockSize >> 2u;
|
||||
|
||||
/* Offset is packed to 32 bit in order to use SIMD32 for addition */
|
||||
offset_packed = __PKHBT(offset, offset, 16);
|
||||
|
||||
/* First part of the processing with loop unrolling. Compute 4 outputs at a time.
|
||||
** a second loop below computes the remaining 1 to 3 samples. */
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* C = A + offset */
|
||||
/* Add offset and then store the results in the destination buffer, 2 samples at a time. */
|
||||
*__SIMD32(pDst)++ = __QADD16(*__SIMD32(pSrc)++, offset_packed);
|
||||
*__SIMD32(pDst)++ = __QADD16(*__SIMD32(pSrc)++, offset_packed);
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
/* If the blockSize is not a multiple of 4, compute any remaining output samples here.
|
||||
** No loop unrolling is used. */
|
||||
blkCnt = blockSize % 0x4u;
|
||||
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* C = A + offset */
|
||||
/* Add offset and then store the results in the destination buffer. */
|
||||
*pDst++ = (q15_t) __QADD16(*pSrc++, offset);
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Run the below code for Cortex-M0 */
|
||||
|
||||
/* Initialize blkCnt with number of samples */
|
||||
blkCnt = blockSize;
|
||||
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* C = A + offset */
|
||||
/* Add offset and then store the results in the destination buffer. */
|
||||
*pDst++ = (q15_t) __SSAT(((q31_t) * pSrc++ + offset), 16);
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
#endif /* #ifndef ARM_MATH_CM0_FAMILY */
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @} end of offset group
|
||||
*/
|
|
@ -0,0 +1,134 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* Copyright (C) 2010-2014 ARM Limited. All rights reserved.
|
||||
*
|
||||
* $Date: 19. March 2015
|
||||
* $Revision: V.1.4.5
|
||||
*
|
||||
* Project: CMSIS DSP Library
|
||||
* Title: arm_q15_to_float.c
|
||||
*
|
||||
* Description: Converts the elements of the Q15 vector to floating-point vector.
|
||||
*
|
||||
* Target Processor: Cortex-M4/Cortex-M3/Cortex-M0
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
* - Neither the name of ARM LIMITED 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 OWNER 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.
|
||||
* ---------------------------------------------------------------------------- */
|
||||
|
||||
#include "arm_math.h"
|
||||
|
||||
/**
|
||||
* @ingroup groupSupport
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup q15_to_x Convert 16-bit Integer value
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup q15_to_x
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Converts the elements of the Q15 vector to floating-point vector.
|
||||
* @param[in] *pSrc points to the Q15 input vector
|
||||
* @param[out] *pDst points to the floating-point output vector
|
||||
* @param[in] blockSize length of the input vector
|
||||
* @return none.
|
||||
*
|
||||
* \par Description:
|
||||
*
|
||||
* The equation used for the conversion process is:
|
||||
*
|
||||
* <pre>
|
||||
* pDst[n] = (float32_t) pSrc[n] / 32768; 0 <= n < blockSize.
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
void arm_q15_to_float(
|
||||
q15_t * pSrc,
|
||||
float32_t * pDst,
|
||||
uint32_t blockSize)
|
||||
{
|
||||
q15_t *pIn = pSrc; /* Src pointer */
|
||||
uint32_t blkCnt; /* loop counter */
|
||||
|
||||
|
||||
#ifndef ARM_MATH_CM0_FAMILY
|
||||
|
||||
/* Run the below code for Cortex-M4 and Cortex-M3 */
|
||||
|
||||
/*loop Unrolling */
|
||||
blkCnt = blockSize >> 2u;
|
||||
|
||||
/* First part of the processing with loop unrolling. Compute 4 outputs at a time.
|
||||
** a second loop below computes the remaining 1 to 3 samples. */
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* C = (float32_t) A / 32768 */
|
||||
/* convert from q15 to float and then store the results in the destination buffer */
|
||||
*pDst++ = ((float32_t) * pIn++ / 32768.0f);
|
||||
*pDst++ = ((float32_t) * pIn++ / 32768.0f);
|
||||
*pDst++ = ((float32_t) * pIn++ / 32768.0f);
|
||||
*pDst++ = ((float32_t) * pIn++ / 32768.0f);
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
|
||||
/* If the blockSize is not a multiple of 4, compute any remaining output samples here.
|
||||
** No loop unrolling is used. */
|
||||
blkCnt = blockSize % 0x4u;
|
||||
|
||||
#else
|
||||
|
||||
/* Run the below code for Cortex-M0 */
|
||||
|
||||
/* Loop over blockSize number of values */
|
||||
blkCnt = blockSize;
|
||||
|
||||
#endif /* #ifndef ARM_MATH_CM0_FAMILY */
|
||||
|
||||
while(blkCnt > 0u)
|
||||
{
|
||||
/* C = (float32_t) A / 32768 */
|
||||
/* convert from q15 to float and then store the results in the destination buffer */
|
||||
*pDst++ = ((float32_t) * pIn++ / 32768.0f);
|
||||
|
||||
/* Decrement the loop counter */
|
||||
blkCnt--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} end of q15_to_x group
|
||||
*/
|
46
Src/main.c
46
Src/main.c
|
@ -52,7 +52,7 @@
|
|||
#include "cmsis_os.h"
|
||||
|
||||
/* USER CODE BEGIN Includes */
|
||||
|
||||
#include <stdint.h>
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
|
@ -80,10 +80,10 @@ DMA_HandleTypeDef hdma_usart2_rx;
|
|||
DMA_HandleTypeDef hdma_usart2_tx;
|
||||
|
||||
osThreadId defaultTaskHandle;
|
||||
uint32_t defaultTaskBuffer[ 256 ];
|
||||
uint32_t defaultTaskBuffer[ 64 ];
|
||||
osStaticThreadDef_t defaultTaskControlBlock;
|
||||
osThreadId ioEventTaskHandle;
|
||||
uint32_t ioEventTaskBuffer[ 384 ];
|
||||
uint32_t ioEventTaskBuffer[ 576 ];
|
||||
osStaticThreadDef_t ioEventTaskControlBlock;
|
||||
osThreadId audioInputTaskHandle;
|
||||
uint32_t audioInputTaskBuffer[ 512 ];
|
||||
|
@ -122,6 +122,8 @@ osTimerId beaconTimer4Handle;
|
|||
|
||||
/* USER CODE BEGIN PV */
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
char serial_number_64[17] = {0};
|
||||
char error_message[80] __attribute__((section(".bss3"))) = {0};
|
||||
|
||||
/* USER CODE END PV */
|
||||
|
||||
|
@ -154,6 +156,31 @@ extern void onBeaconTimer4(void const * argument);
|
|||
|
||||
/* USER CODE BEGIN 0 */
|
||||
|
||||
/*
|
||||
* Same algorithm as here: https://github.com/libopencm3/libopencm3/blob/master/lib/stm32/desig.c
|
||||
*/
|
||||
|
||||
void encode_serial_number()
|
||||
{
|
||||
uint8_t *uid = (uint8_t *)UID_BASE;
|
||||
|
||||
uint8_t serial[6];
|
||||
serial[0] = uid[11];
|
||||
serial[1] = uid[10] + uid[2];
|
||||
serial[2] = uid[9];
|
||||
serial[3] = uid[8] + uid[0];
|
||||
serial[4] = uid[7];
|
||||
serial[5] = uid[6];
|
||||
|
||||
snprintf(
|
||||
serial_number_64,
|
||||
sizeof(serial_number_64),
|
||||
"%02X%02X%02X%02X%02X%02X",
|
||||
serial[0], serial[1], serial[2],
|
||||
serial[3], serial[4], serial[5]
|
||||
);
|
||||
}
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
||||
/**
|
||||
|
@ -196,7 +223,7 @@ int main(void)
|
|||
MX_USART2_UART_Init();
|
||||
MX_I2C3_Init();
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
encode_serial_number();
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
|
@ -230,11 +257,11 @@ int main(void)
|
|||
|
||||
/* Create the thread(s) */
|
||||
/* definition and creation of defaultTask */
|
||||
osThreadStaticDef(defaultTask, startDefaultTask, osPriorityIdle, 0, 256, defaultTaskBuffer, &defaultTaskControlBlock);
|
||||
osThreadStaticDef(defaultTask, startDefaultTask, osPriorityIdle, 0, 64, defaultTaskBuffer, &defaultTaskControlBlock);
|
||||
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
|
||||
|
||||
/* definition and creation of ioEventTask */
|
||||
osThreadStaticDef(ioEventTask, startIOEventTask, osPriorityLow, 0, 384, ioEventTaskBuffer, &ioEventTaskControlBlock);
|
||||
osThreadStaticDef(ioEventTask, startIOEventTask, osPriorityLow, 0, 576, ioEventTaskBuffer, &ioEventTaskControlBlock);
|
||||
ioEventTaskHandle = osThreadCreate(osThread(ioEventTask), NULL);
|
||||
|
||||
/* definition and creation of audioInputTask */
|
||||
|
@ -439,7 +466,7 @@ static void MX_ADC1_Init(void)
|
|||
*/
|
||||
sConfig.Channel = ADC_CHANNEL_8;
|
||||
sConfig.Rank = ADC_REGULAR_RANK_1;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
|
||||
sConfig.SingleDiff = ADC_SINGLE_ENDED;
|
||||
sConfig.OffsetNumber = ADC_OFFSET_NONE;
|
||||
sConfig.Offset = 0;
|
||||
|
@ -453,7 +480,6 @@ static void MX_ADC1_Init(void)
|
|||
/* CRC init function */
|
||||
static void MX_CRC_Init(void)
|
||||
{
|
||||
|
||||
hcrc.Instance = CRC;
|
||||
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
|
||||
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
|
||||
|
@ -512,7 +538,7 @@ static void MX_I2C3_Init(void)
|
|||
{
|
||||
|
||||
hi2c3.Instance = I2C3;
|
||||
hi2c3.Init.Timing = 0x20000209;
|
||||
hi2c3.Init.Timing = 0x2010091A;
|
||||
hi2c3.Init.OwnAddress1 = 0;
|
||||
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
|
||||
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
|
||||
|
@ -864,7 +890,7 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
|||
* @param line: The line in file as a number.
|
||||
* @retval None
|
||||
*/
|
||||
void _Error_Handler(char *file, int line)
|
||||
void _Error_Handler(const char *file, int line)
|
||||
{
|
||||
/* USER CODE BEGIN Error_Handler_Debug */
|
||||
/* User can add his own implementation to report the HAL error return state */
|
||||
|
|
|
@ -61,7 +61,7 @@ extern DMA_HandleTypeDef hdma_usart2_rx;
|
|||
|
||||
extern DMA_HandleTypeDef hdma_usart2_tx;
|
||||
|
||||
extern void _Error_Handler(char *, int);
|
||||
extern void _Error_Handler(const char *, int);
|
||||
/* USER CODE BEGIN 0 */
|
||||
|
||||
/* USER CODE END 0 */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Robert C. Riggs <rob@pangalactic.org>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__TNC__AFSK_MODULATOR_HPP_
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
|
||||
#define MOBILINKD__TNC__AFSKTESTTONE_HPP_
|
||||
#define MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
|
||||
|
||||
#include "cmsis_os.h"
|
||||
|
||||
|
@ -29,4 +29,4 @@ struct AFSKTestTone
|
|||
}} // mobilinkd::tnc
|
||||
|
||||
|
||||
#endif // MOBILINKD__TNC__AFSKTESTTONE_HPP_
|
||||
#endif // MOBILINKD__TNC__AFSK_TEST_TONE_HPP_
|
||||
|
|
|
@ -1,41 +1,42 @@
|
|||
// Copyright 2017 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2017-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "AfskDemodulator.hpp"
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace afsk1200 {
|
||||
|
||||
|
||||
hdlc::IoFrame* Demodulator::operator()(float* samples, size_t len)
|
||||
hdlc::IoFrame* Demodulator::operator()(q15_t* samples, size_t len)
|
||||
{
|
||||
using namespace mobilinkd::tnc::gpio;
|
||||
|
||||
hdlc::IoFrame* result = 0;
|
||||
|
||||
float* fa = audio_filter_(samples);
|
||||
std::transform(fa, fa + audio::ADC_BUFFER_SIZE, buffer_a, agc_);
|
||||
auto levels = input_comparator_(buffer_a);
|
||||
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
buffer_[i] = int16_t(fa[i]);
|
||||
}
|
||||
|
||||
bool delayed = delay_line_(levels[i]);
|
||||
float_type x = float_type(levels[i] ^ delayed) - .5f;
|
||||
float_type fc = correlator_filter_(x);
|
||||
bool bit = output_comparator_(fc);
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
bool level = (buffer_[i] >= 0);
|
||||
bool delayed = delay_line_(level);
|
||||
buffer_[i] = (int16_t(level ^ delayed) << 1) - 1;
|
||||
}
|
||||
|
||||
auto* fc = lpf_filter_.filter(buffer_);
|
||||
|
||||
for (size_t i = 0; i != len; i++) {
|
||||
bool bit = fc[i] >= 0;
|
||||
auto pll = pll_(bit);
|
||||
|
||||
if (pll.sample) {
|
||||
if (locked_ != pll.locked) {
|
||||
locked_ = pll.locked;
|
||||
}
|
||||
locked_ = pll.locked;
|
||||
|
||||
// We will only ever get one frame because there are
|
||||
// not enough bits in a block for more than one.
|
||||
if (result) {
|
||||
auto tmp = hdlc_decoder_(nrzi_.decode(bit), pll.locked);
|
||||
if (tmp) hdlc::ioFramePool().release(tmp);
|
||||
auto tmp = hdlc_decoder_(nrzi_.decode(bit), true);
|
||||
if (tmp) hdlc::release(tmp);
|
||||
} else {
|
||||
result = hdlc_decoder_(nrzi_.decode(bit), pll.locked);
|
||||
result = hdlc_decoder_(nrzi_.decode(bit), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +1,37 @@
|
|||
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__AFSK_DEMODULATOR_HPP_
|
||||
#define MOBILINKD__AFSK_DEMODULATOR_HPP_
|
||||
|
||||
#include <arm_math.h>
|
||||
#include "AGC.hpp"
|
||||
#include "DelayLine.hpp"
|
||||
#include "AudioInput.hpp"
|
||||
#include "DigitalPLL.hpp"
|
||||
// #include "Filter.h"
|
||||
#include "HdlcDecoder.hpp"
|
||||
#include "Hysteresis.hpp"
|
||||
#include "FirFilter.hpp"
|
||||
#include "IirFilter.hpp"
|
||||
#include "NRZI.hpp"
|
||||
#include "GPIO.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace afsk1200 {
|
||||
|
||||
#if 0
|
||||
const float b[] = {
|
||||
4.57519926037e-06,
|
||||
2.28759963018e-05,
|
||||
4.57519926037e-05,
|
||||
4.57519926037e-05,
|
||||
2.28759963018e-05,
|
||||
4.57519926037e-06,
|
||||
};
|
||||
const size_t LPF_FILTER_LEN = 96;
|
||||
|
||||
const float a[] = {
|
||||
1.0,
|
||||
-4.41489189545,
|
||||
7.82710410154,
|
||||
-6.96306748269,
|
||||
3.10736843037,
|
||||
-0.556366747391,
|
||||
};
|
||||
|
||||
// Bessel 760Hz
|
||||
const float b[] = {
|
||||
4.36034607e-06,
|
||||
2.18017304e-05,
|
||||
4.36034607e-05,
|
||||
4.36034607e-05,
|
||||
2.18017304e-05,
|
||||
4.36034607e-06,
|
||||
};
|
||||
|
||||
const float a[] = {
|
||||
1.0,
|
||||
-4.32673235,
|
||||
7.51393353,
|
||||
-6.54579279,
|
||||
2.86009139,
|
||||
-0.50136025,
|
||||
};
|
||||
#endif
|
||||
|
||||
// Bessel 1200Hz 7-pole low-pass
|
||||
const float b[] = {
|
||||
6.10481382e-07,
|
||||
4.27336967e-06,
|
||||
1.28201090e-05,
|
||||
2.13668484e-05,
|
||||
2.13668484e-05,
|
||||
1.28201090e-05,
|
||||
4.27336967e-06,
|
||||
6.10481382e-07,
|
||||
};
|
||||
|
||||
const float a[] = {
|
||||
1.0,
|
||||
-5.56209875,
|
||||
13.34528507,
|
||||
-17.89828744,
|
||||
14.48661262,
|
||||
-7.07391246,
|
||||
1.92904679,
|
||||
-0.22656769,
|
||||
const q15_t lpf_coeffs[] = {
|
||||
0, 1, 3, 5, 8, 11, 14, 17, 19, 20, 18, 14,
|
||||
7, -2, -16, -33, -53, -76, -101, -126, -151, -174, -194, -208,
|
||||
-215, -212, -199, -173, -133, -79, -10, 74, 173, 287, 413, 549,
|
||||
693, 842, 993, 1142, 1287, 1423, 1547, 1656, 1747, 1817, 1865, 1889,
|
||||
1889, 1865, 1817, 1747, 1656, 1547, 1423, 1287, 1142, 993, 842, 693,
|
||||
549, 413, 287, 173, 74, -10, -79, -133, -173, -199, -212, -215,
|
||||
-208, -194, -174, -151, -126, -101, -76, -53, -33, -16, -2, 7,
|
||||
14, 18, 20, 19, 17, 14, 11, 8, 5, 3, 1, 0,
|
||||
};
|
||||
|
||||
typedef FirFilter<audio::ADC_BUFFER_SIZE, 9> emphasis_filter_type;
|
||||
// typedef IirFilter<audio::ADC_BUFFER_SIZE, 2> emphasis_filter_type;
|
||||
|
||||
struct Demodulator {
|
||||
|
||||
|
@ -94,36 +40,30 @@ struct Demodulator {
|
|||
typedef std::function<float_type(float_type)> filter_function_type;
|
||||
|
||||
static const size_t SYMBOL_RATE = 1200;
|
||||
static const size_t BUFFER_SIZE = 330;
|
||||
|
||||
typedef BaseDigitalPLL<float_type> DPLL;
|
||||
|
||||
size_t sample_rate_;
|
||||
emphasis_filter_type& audio_filter_;
|
||||
libafsk::BaseAGC<float_type> agc_;
|
||||
libafsk::BlockHysteresis<bool, audio::ADC_BUFFER_SIZE> input_comparator_;
|
||||
libafsk::FixedDelayLine<40> delay_line_;
|
||||
IirFilter<8> correlator_filter_;
|
||||
libafsk::BaseHysteresis<float_type> output_comparator_;
|
||||
DPLL pll_;
|
||||
Q15FirFilter<audio::ADC_BUFFER_SIZE, LPF_FILTER_LEN> lpf_filter_;
|
||||
libafsk::NRZI nrzi_;
|
||||
hdlc::Decoder hdlc_decoder_;
|
||||
hdlc::NewDecoder hdlc_decoder_;
|
||||
bool locked_;
|
||||
float_type buffer_a[audio::ADC_BUFFER_SIZE];
|
||||
q15_t buffer_[audio::ADC_BUFFER_SIZE];
|
||||
|
||||
Demodulator(size_t sample_rate, emphasis_filter_type& c)
|
||||
: sample_rate_(sample_rate)
|
||||
, audio_filter_(c)
|
||||
, agc_(.01, .001, 0.3, 1000.0)
|
||||
, input_comparator_(-0.0005, 0.0005)
|
||||
, delay_line_(sample_rate, 0.000448)
|
||||
, correlator_filter_(b, a)
|
||||
, output_comparator_(-0.05, 0.05)
|
||||
, pll_(sample_rate, SYMBOL_RATE)
|
||||
, nrzi_(), hdlc_decoder_(false), locked_(false)
|
||||
{}
|
||||
{
|
||||
lpf_filter_.init(lpf_coeffs);
|
||||
}
|
||||
|
||||
hdlc::IoFrame* operator()(float* samples, size_t len);
|
||||
hdlc::IoFrame* operator()(q15_t* samples, size_t len);
|
||||
|
||||
bool locked() const {return locked_;}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2018-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "AudioInput.hpp"
|
||||
|
@ -99,11 +99,11 @@ extern "C" void startAudioInputTask(void const*) {
|
|||
break;
|
||||
case STREAM_AVERAGE_TWIST_LEVEL:
|
||||
DEBUG("STREAM_AVERAGE_TWIST_LEVEL");
|
||||
streamAverageInputTwist();
|
||||
// streamAverageInputTwist();
|
||||
break;
|
||||
case STREAM_INSTANT_TWIST_LEVEL:
|
||||
DEBUG("STREAM_INSTANT_TWIST_LEVEL");
|
||||
streamInstantInputTwist();
|
||||
// streamInstantInputTwist();
|
||||
break;
|
||||
case AUTO_ADJUST_INPUT_LEVEL:
|
||||
DEBUG("AUTO_ADJUST_INPUT_LEVEL");
|
||||
|
@ -129,260 +129,42 @@ extern "C" void startAudioInputTask(void const*) {
|
|||
namespace mobilinkd { namespace tnc { namespace audio {
|
||||
|
||||
/*
|
||||
|
||||
FIR filter designed with
|
||||
http://t-filter.appspot.com
|
||||
|
||||
sampling frequency: 26400 Hz
|
||||
|
||||
* 0 Hz - 800 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -41.713187739640446 dB
|
||||
|
||||
* 1100 Hz - 2300 Hz
|
||||
gain = 1
|
||||
desired ripple = 3 dB
|
||||
actual ripple = 1.9403554103597218 dB
|
||||
|
||||
* 2600 Hz - 13200 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -41.713187739640446 dB
|
||||
|
||||
*/
|
||||
|
||||
#define FILTER_TAP_NUM 121
|
||||
|
||||
const float taps_0dB_121[] = {
|
||||
0.00404434702588704,
|
||||
-0.0003678805989470367,
|
||||
-0.0011037474176397869,
|
||||
-0.0023718433790735397,
|
||||
-0.004074774206090812,
|
||||
-0.005873042355767296,
|
||||
-0.007185024927682025,
|
||||
-0.00733586918005499,
|
||||
-0.005849936137673611,
|
||||
-0.0026340821242355635,
|
||||
0.001829866887380395,
|
||||
0.006596559367932984,
|
||||
0.010513363703436297,
|
||||
0.012581963716759209,
|
||||
0.012278717176141912,
|
||||
0.009711068794837135,
|
||||
0.005595156551222992,
|
||||
0.0010151465722928203,
|
||||
-0.0028911100291917724,
|
||||
-0.00525632756952279,
|
||||
-0.005713225280738633,
|
||||
-0.004481173891886628,
|
||||
-0.0022979447479362165,
|
||||
-0.00020042687909196258,
|
||||
0.0007698191454281245,
|
||||
-0.00010521900913954391,
|
||||
-0.0029591195718788473,
|
||||
-0.00722329412770922,
|
||||
-0.01171525034180743,
|
||||
-0.014940096229612041,
|
||||
-0.015533747313789608,
|
||||
-0.012735965353880947,
|
||||
-0.006719733175529481,
|
||||
0.0013488096154956725,
|
||||
0.00958058819325866,
|
||||
0.015905894530456915,
|
||||
0.018729580500272548,
|
||||
0.01748781569748245,
|
||||
0.012897342590446394,
|
||||
0.006795493031300682,
|
||||
0.0015881037163025886,
|
||||
-0.0005374299552534915,
|
||||
0.0016047331197704602,
|
||||
0.007673729209166328,
|
||||
0.015724242346878387,
|
||||
0.022631203832237833,
|
||||
0.024935722640487233,
|
||||
0.01989507252801227,
|
||||
0.00645078432931977,
|
||||
-0.014172629267618218,
|
||||
-0.038442876378037664,
|
||||
-0.06113274585379875,
|
||||
-0.07647113797360244,
|
||||
-0.07957164384983365,
|
||||
-0.06778356575105521,
|
||||
-0.04159845430900078,
|
||||
-0.0048373906972505945,
|
||||
0.03598583922416374,
|
||||
0.0730150987796154,
|
||||
0.09880455186134979,
|
||||
0.10804223448811107,
|
||||
0.09880455186134979,
|
||||
0.0730150987796154,
|
||||
0.03598583922416374,
|
||||
-0.0048373906972505945,
|
||||
-0.04159845430900078,
|
||||
-0.06778356575105521,
|
||||
-0.07957164384983365,
|
||||
-0.07647113797360244,
|
||||
-0.06113274585379875,
|
||||
-0.038442876378037664,
|
||||
-0.014172629267618218,
|
||||
0.006450784329319771,
|
||||
0.01989507252801227,
|
||||
0.024935722640487233,
|
||||
0.022631203832237833,
|
||||
0.015724242346878387,
|
||||
0.007673729209166332,
|
||||
0.0016047331197704602,
|
||||
-0.0005374299552534915,
|
||||
0.0015881037163025886,
|
||||
0.0067954930313006805,
|
||||
0.012897342590446394,
|
||||
0.017487815697482458,
|
||||
0.01872958050027255,
|
||||
0.015905894530456915,
|
||||
0.00958058819325866,
|
||||
0.0013488096154956762,
|
||||
-0.006719733175529481,
|
||||
-0.012735965353880947,
|
||||
-0.015533747313789608,
|
||||
-0.014940096229612034,
|
||||
-0.01171525034180743,
|
||||
-0.00722329412770922,
|
||||
-0.0029591195718788473,
|
||||
-0.00010521900913954758,
|
||||
0.0007698191454281245,
|
||||
-0.00020042687909196258,
|
||||
-0.0022979447479362165,
|
||||
-0.004481173891886626,
|
||||
-0.005713225280738633,
|
||||
-0.005256327569522788,
|
||||
-0.0028911100291917724,
|
||||
0.0010151465722928203,
|
||||
0.005595156551222992,
|
||||
0.009711068794837135,
|
||||
0.012278717176141915,
|
||||
0.012581963716759209,
|
||||
0.010513363703436297,
|
||||
0.006596559367932984,
|
||||
0.001829866887380395,
|
||||
-0.0026340821242355635,
|
||||
-0.005849936137673611,
|
||||
-0.0073358691800549875,
|
||||
-0.007185024927682024,
|
||||
-0.005873042355767296,
|
||||
-0.004074774206090811,
|
||||
-0.0023718433790735397,
|
||||
-0.0011037474176397869,
|
||||
-0.00036788059894704404,
|
||||
0.00404434702588704
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
FIR filter designed with
|
||||
http://t-filter.appspot.com
|
||||
|
||||
sampling frequency: 26400 Hz
|
||||
|
||||
* 0 Hz - 600 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -41.59537969202882 dB
|
||||
|
||||
* 1100 Hz - 2300 Hz
|
||||
gain = 1
|
||||
desired ripple = 3 dB
|
||||
actual ripple = 1.9670775534013671 dB
|
||||
|
||||
* 2800 Hz - 13200 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -41.59537969202882 dB
|
||||
|
||||
*/
|
||||
|
||||
// #define FILTER_TAP_NUM 73
|
||||
|
||||
const float taps_0dB_73[] = {
|
||||
0.0010893641938196257,
|
||||
0.0029403198794405202,
|
||||
-0.0037231874681753637,
|
||||
-0.005078116094780293,
|
||||
-0.008797286521082463,
|
||||
-0.011317340935878852,
|
||||
-0.012200463385017889,
|
||||
-0.01036925371439487,
|
||||
-0.005637326405238566,
|
||||
0.0014334055832832988,
|
||||
0.009462055516437227,
|
||||
0.016585173167785613,
|
||||
0.020968649539195763,
|
||||
0.021402512805125434,
|
||||
0.017768177789191805,
|
||||
0.011189277365350641,
|
||||
0.003796773667470304,
|
||||
-0.0019035128640327481,
|
||||
-0.003853114272608765,
|
||||
-0.0012626334798333488,
|
||||
0.004945485136075468,
|
||||
0.012177421685799305,
|
||||
0.016832103112238102,
|
||||
0.01536679512873413,
|
||||
0.005573647945409955,
|
||||
-0.012436210925634471,
|
||||
-0.03581550676890827,
|
||||
-0.05935854007614169,
|
||||
-0.07666569528110105,
|
||||
-0.08180873487685453,
|
||||
-0.07107700533422828,
|
||||
-0.0442469908102239,
|
||||
-0.005044918510227134,
|
||||
0.03944927956419565,
|
||||
0.0803589200537307,
|
||||
0.10908069178917619,
|
||||
0.11940367705752576,
|
||||
0.1090806917891762,
|
||||
0.0803589200537307,
|
||||
0.03944927956419565,
|
||||
-0.005044918510227135,
|
||||
-0.0442469908102239,
|
||||
-0.07107700533422828,
|
||||
-0.08180873487685453,
|
||||
-0.07666569528110105,
|
||||
-0.05935854007614169,
|
||||
-0.03581550676890827,
|
||||
-0.012436210925634473,
|
||||
0.005573647945409955,
|
||||
0.015366795128734134,
|
||||
0.016832103112238102,
|
||||
0.012177421685799305,
|
||||
0.004945485136075468,
|
||||
-0.0012626334798333488,
|
||||
-0.003853114272608765,
|
||||
-0.001903512864032745,
|
||||
0.003796773667470304,
|
||||
0.011189277365350641,
|
||||
0.017768177789191805,
|
||||
0.021402512805125434,
|
||||
0.020968649539195763,
|
||||
0.016585173167785607,
|
||||
0.009462055516437227,
|
||||
0.0014334055832832988,
|
||||
-0.005637326405238568,
|
||||
-0.01036925371439487,
|
||||
-0.012200463385017889,
|
||||
-0.011317340935878852,
|
||||
-0.008797286521082463,
|
||||
-0.005078116094780293,
|
||||
-0.0037231874681753637,
|
||||
0.0029403198794405202,
|
||||
0.0010893641938196242
|
||||
* Generated with Scipy Filter, 152 coefficients, 1100-2300Hz bandpass,
|
||||
* Hann window, starting and ending 0 value coefficients removed.
|
||||
*
|
||||
* np.array(
|
||||
* firwin2(152,
|
||||
* [
|
||||
* 0.0,
|
||||
* 1000.0/(sample_rate/2),
|
||||
* 1100.0/(sample_rate/2),
|
||||
* 2350.0/(sample_rate/2),
|
||||
* 2500.0/(sample_rate/2),
|
||||
* 1.0
|
||||
* ],
|
||||
* [0,0,1,1,0,0],
|
||||
* antisymmetric = False,
|
||||
* window='hann') * 32768,
|
||||
* dtype=int)[10:-10]
|
||||
*/
|
||||
constexpr size_t FILTER_TAP_NUM = 132;
|
||||
const q15_t bpf_coeffs[] = {
|
||||
4, 0, -5, -10, -13, -12, -9, -4, -2, -4, -12, -26,
|
||||
-41, -52, -51, -35, -3, 39, 83, 117, 131, 118, 83, 36,
|
||||
-6, -32, -30, -3, 36, 67, 66, 19, -74, -199, -323, -408,
|
||||
-421, -344, -187, 17, 218, 364, 417, 369, 247, 106, 14, 26,
|
||||
166, 407, 676, 865, 866, 605, 68, -675, -1484, -2171, -2547, -2471,
|
||||
-1895, -882, 394, 1692, 2747, 3337, 3337, 2747, 1692, 394, -882, -1895,
|
||||
-2471, -2547, -2171, -1484, -675, 68, 605, 866, 865, 676, 407, 166,
|
||||
26, 14, 106, 247, 369, 417, 364, 218, 17, -187, -344, -421,
|
||||
-408, -323, -199, -74, 19, 66, 67, 36, -3, -30, -32, -6,
|
||||
36, 83, 118, 131, 117, 83, 39, -3, -35, -51, -52, -41,
|
||||
-26, -12, -4, -2, -4, -9, -12, -13, -10, -5, 0, 4,
|
||||
};
|
||||
|
||||
uint32_t adc_buffer[ADC_BUFFER_SIZE]; // Two samples per element.
|
||||
|
||||
typedef FirFilter<ADC_BUFFER_SIZE, FILTER_TAP_NUM> audio_filter_type;
|
||||
typedef Q15FirFilter<ADC_BUFFER_SIZE, FILTER_TAP_NUM> audio_filter_type;
|
||||
|
||||
audio_filter_type audio_filter;
|
||||
|
||||
|
@ -412,12 +194,13 @@ mobilinkd::tnc::afsk1200::Demodulator& getDemod3(const TFirCoefficients<9>& f) {
|
|||
return instance;
|
||||
}
|
||||
|
||||
q15_t normalized[ADC_BUFFER_SIZE];
|
||||
|
||||
void demodulatorTask() {
|
||||
|
||||
DEBUG("enter demodulatorTask");
|
||||
|
||||
audio_filter.init(taps_0dB_121);
|
||||
audio_filter.init(bpf_coeffs);
|
||||
|
||||
// rx_twist is 6dB for discriminator input and 0db for de-emphasized input.
|
||||
auto twist = kiss::settings().rx_twist;
|
||||
|
@ -437,7 +220,6 @@ void demodulatorTask() {
|
|||
bool dcd_status{false};
|
||||
|
||||
while (true) {
|
||||
mobilinkd::tnc::gpio::LD3::off();
|
||||
osEvent peek = osMessagePeek(audioInputQueueHandle, 0);
|
||||
if (peek.status == osEventMessage) break;
|
||||
|
||||
|
@ -445,14 +227,15 @@ void demodulatorTask() {
|
|||
if (evt.status != osEventMessage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
++counter;
|
||||
mobilinkd::tnc::gpio::LD3::on();
|
||||
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.p;
|
||||
auto samples = (int16_t*) block->buffer;
|
||||
|
||||
float* audio = audio_filter(samples);
|
||||
arm_offset_q15(samples, 0 - virtual_ground, normalized, ADC_BUFFER_SIZE);
|
||||
adcPool.deallocate(block);
|
||||
q15_t* audio = audio_filter(normalized);
|
||||
|
||||
#if 1
|
||||
frame = demod1(audio, ADC_BUFFER_SIZE);
|
||||
|
@ -463,11 +246,11 @@ void demodulatorTask() {
|
|||
last_fcs = save_fcs;
|
||||
last_counter = counter;
|
||||
} else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -481,11 +264,11 @@ void demodulatorTask() {
|
|||
last_fcs = save_fcs;
|
||||
last_counter = counter;
|
||||
} else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -499,11 +282,11 @@ void demodulatorTask() {
|
|||
last_fcs = save_fcs;
|
||||
last_counter = counter;
|
||||
} else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
else {
|
||||
hdlc::ioFramePool().release(frame);
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -538,8 +321,8 @@ void streamLevels(uint32_t channel, uint8_t cmd) {
|
|||
|
||||
uint16_t count = 0;
|
||||
uint32_t accum = 0;
|
||||
uint16_t min = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t max = std::numeric_limits<uint16_t>::min();
|
||||
uint16_t vmin = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t vmax = std::numeric_limits<uint16_t>::min();
|
||||
|
||||
while (count < 2640) {
|
||||
osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever);
|
||||
|
@ -548,30 +331,30 @@ void streamLevels(uint32_t channel, uint8_t cmd) {
|
|||
count += ADC_BUFFER_SIZE;
|
||||
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.p;
|
||||
uint16_t* start = (uint16_t*) block->buffer;
|
||||
uint16_t* end = start + ADC_BUFFER_SIZE;
|
||||
auto start = (uint16_t*) block->buffer;
|
||||
auto end = start + ADC_BUFFER_SIZE;
|
||||
|
||||
min = std::min(min, *std::min_element(start, end));
|
||||
max = std::max(max, *std::max_element(start, end));
|
||||
vmin = std::min(vmin, *std::min_element(start, end));
|
||||
vmax = std::max(vmax, *std::max_element(start, end));
|
||||
accum = std::accumulate(start, end, accum);
|
||||
|
||||
adcPool.deallocate(block);
|
||||
}
|
||||
|
||||
uint16_t pp = (max - min) << 4;
|
||||
uint16_t pp = (vmax - vmin) << 4;
|
||||
uint16_t avg = (accum / count) << 4;
|
||||
min <<= 4;
|
||||
max <<= 4;
|
||||
vmin <<= 4;
|
||||
vmax <<= 4;
|
||||
|
||||
data[0] = cmd;
|
||||
data[1] = (pp >> 8) & 0xFF; // Vpp
|
||||
data[2] = (pp & 0xFF);
|
||||
data[3] = (avg >> 8) & 0xFF; // Vavg (DC level)
|
||||
data[4] = (avg & 0xFF);
|
||||
data[5] = (min >> 8) & 0xFF; // Vmin
|
||||
data[6] = (min & 0xFF);
|
||||
data[7] = (max >> 8) & 0xFF; // Vmax
|
||||
data[8] = (max & 0xFF);
|
||||
data[5] = (vmin >> 8) & 0xFF; // Vmin
|
||||
data[6] = (vmin & 0xFF);
|
||||
data[7] = (vmax >> 8) & 0xFF; // Vmax
|
||||
data[8] = (vmax & 0xFF);
|
||||
|
||||
ioport->write(data, 9, 6, 10);
|
||||
}
|
||||
|
@ -600,11 +383,9 @@ levels_type readLevels(uint32_t channel, uint32_t samples) {
|
|||
if (evt.status != osEventMessage) continue;
|
||||
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.p;
|
||||
auto start = reinterpret_cast<uint16_t*>(block->buffer);
|
||||
auto start = (uint16_t*) block->buffer;
|
||||
auto end = start + ADC_BUFFER_SIZE;
|
||||
|
||||
// if (count == 0) for (auto it = start; it != end; ++it) DEBUG("%hu\n", *it);
|
||||
|
||||
vmin = std::min(vmin, *std::min_element(start, end));
|
||||
vmax = std::max(vmax, *std::max_element(start, end));
|
||||
accum = std::accumulate(start, end, accum);
|
||||
|
@ -623,6 +404,7 @@ levels_type readLevels(uint32_t channel, uint32_t samples) {
|
|||
return levels_type(pp, avg, vmin, vmax);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This provides 100Hz resolution to the Goerztel filter.
|
||||
*/
|
||||
|
@ -649,22 +431,22 @@ float readTwist()
|
|||
|
||||
startADC(channel);
|
||||
|
||||
for (uint32_t i = 0; i != AVG_SAMPLES; ++i) {
|
||||
|
||||
for (uint32_t i = 0; i != AVG_SAMPLES; ++i)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
while (count < TWIST_SAMPLE_SIZE) {
|
||||
|
||||
while (count < TWIST_SAMPLE_SIZE)
|
||||
{
|
||||
osEvent evt = osMessageGet(adcInputQueueHandle, osWaitForever);
|
||||
if (evt.status != osEventMessage) continue;
|
||||
|
||||
count += ADC_BUFFER_SIZE;
|
||||
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.v;
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.p;
|
||||
uint16_t* data = (uint16_t*) block->buffer;
|
||||
gf1200(data, ADC_BUFFER_SIZE);
|
||||
gf2200(data, ADC_BUFFER_SIZE);
|
||||
|
||||
adcPool.deallocate(block);
|
||||
|
||||
count += ADC_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
g1200 += (gf1200 / count);
|
||||
|
@ -733,7 +515,7 @@ void pollInputTwist()
|
|||
|
||||
count += ADC_BUFFER_SIZE;
|
||||
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.v;
|
||||
auto block = (adc_pool_type::chunk_type*) evt.value.p;
|
||||
uint16_t* data = (uint16_t*) block->buffer;
|
||||
gf1200(data, ADC_BUFFER_SIZE);
|
||||
gf2200(data, ADC_BUFFER_SIZE);
|
||||
|
@ -768,6 +550,7 @@ void pollInputTwist()
|
|||
DEBUG("exit pollInputTwist");
|
||||
}
|
||||
|
||||
#if 0
|
||||
void streamAverageInputTwist()
|
||||
{
|
||||
DEBUG("enter streamAverageInputTwist");
|
||||
|
@ -820,7 +603,8 @@ void streamAverageInputTwist()
|
|||
g2700 += 10.0 * log10(gf2700);
|
||||
|
||||
char* buffer = 0;
|
||||
int len = asiprintf(
|
||||
// @TODO: Make re-entrant printf work (or convert to fixed-point).
|
||||
int len = asiprintf_r(
|
||||
&buffer,
|
||||
"_%f, %f, %f, %f, %f\r\n",
|
||||
g700 / acount,
|
||||
|
@ -884,7 +668,8 @@ void streamInstantInputTwist()
|
|||
}
|
||||
|
||||
char* buffer = 0;
|
||||
int len = asiprintf(
|
||||
// @TODO: Make re-entrant printf work (or convert to fixed-point).
|
||||
int len = asiprintf_r(
|
||||
&buffer,
|
||||
"_%f, %f, %f, %f, %f\r\n",
|
||||
10.0 * log10(gf700),
|
||||
|
@ -909,6 +694,7 @@ void streamInstantInputTwist()
|
|||
stopADC();
|
||||
DEBUG("exit streamInstantInputTwist");
|
||||
}
|
||||
#endif
|
||||
|
||||
void streamAmplifiedInputLevels() {
|
||||
DEBUG("enter streamAmplifiedInputLevels");
|
||||
|
@ -942,6 +728,7 @@ void pollAmplifiedInputLevel() {
|
|||
DEBUG("exit pollAmplifiedInputLevel");
|
||||
}
|
||||
|
||||
#if 0
|
||||
void stop() {
|
||||
osDelay(100);
|
||||
#if 0
|
||||
|
@ -971,5 +758,6 @@ void stop() {
|
|||
DEBUG("Wake");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}}} // mobilinkd::tnc::audio
|
||||
|
|
|
@ -28,10 +28,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:
|
||||
*
|
||||
* - AMPLIFIED_AUDIO_IN, ADC1_IN7 -- this is the input used by the demodulator.
|
||||
* - AUDIO_IN, ADC1, CHANNEL_8 -- 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.
|
||||
* pulled low. *** NOT USED on NucleoTNC ***
|
||||
*
|
||||
* These inputs can be measured in a couple of different ways:
|
||||
* - Vavg -- for the average DC level of the signal
|
||||
|
@ -61,7 +61,6 @@ void TNC_Error_Handler(int dev, int err);
|
|||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace audio {
|
||||
|
||||
|
@ -90,9 +89,9 @@ extern uint32_t adc_buffer[]; // Two int16_t samples per element.
|
|||
|
||||
inline void stopADC() {
|
||||
if (HAL_ADC_Stop_DMA(&hadc1) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
if (HAL_TIM_Base_Stop(&htim6) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
}
|
||||
|
||||
inline void startADC(uint32_t channel) {
|
||||
|
@ -101,23 +100,23 @@ inline void startADC(uint32_t channel) {
|
|||
sConfig.Channel = channel;
|
||||
sConfig.Rank = ADC_REGULAR_RANK_1;
|
||||
sConfig.SingleDiff = ADC_SINGLE_ENDED;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_24CYCLES_5;
|
||||
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
|
||||
sConfig.OffsetNumber = ADC_OFFSET_NONE;
|
||||
sConfig.Offset = 0;
|
||||
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
|
||||
if (HAL_TIM_Base_Start(&htim6) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
if (HAL_ADC_Start_DMA(&hadc1, adc_buffer, ADC_BUFFER_SIZE * 2) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
}
|
||||
|
||||
inline void restartADC() {
|
||||
if (HAL_TIM_Base_Start(&htim6) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
if (HAL_ADC_Start_DMA(&hadc1, adc_buffer, ADC_BUFFER_SIZE * 2) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
}
|
||||
|
||||
/// Vpp, Vavg, Vmin, Vmax
|
||||
|
@ -138,4 +137,6 @@ void streamInstantInputTwist();
|
|||
|
||||
}}} // mobilinkd::tnc::audio
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // MOBILINKD__TNC__AUDIO__INPUT_HPP_
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright 2017 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2018 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "AudioLevel.hpp"
|
||||
#include "AudioInput.hpp"
|
||||
#include "ModulatorTask.hpp"
|
||||
#include "KissHardware.hpp"
|
||||
#include "ModulatorTask.hpp"
|
||||
#include "GPIO.hpp"
|
||||
#include "Led.h"
|
||||
#include "ModulatorTask.hpp"
|
||||
|
||||
#include "main.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
|
@ -23,27 +24,23 @@ extern DAC_HandleTypeDef hdac1;
|
|||
|
||||
namespace mobilinkd { namespace tnc { namespace audio {
|
||||
|
||||
uint16_t virtual_ground;
|
||||
float i_vgnd;
|
||||
|
||||
void setAudioPins(void) {
|
||||
GPIO_InitTypeDef GPIO_InitStruct;
|
||||
|
||||
GPIO_InitStruct.Pin = AUDIO_IN_Pin;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(AUDIO_IN_GPIO_Port, &GPIO_InitStruct);
|
||||
}
|
||||
uint16_t virtual_ground{0};
|
||||
float i_vgnd{0.0f};
|
||||
|
||||
void set_input_gain(int level)
|
||||
{
|
||||
uint32_t dc_offset{};
|
||||
|
||||
// Stop and de-init the op amp before changing its state.
|
||||
if (HAL_OPAMP_Stop(&hopamp1) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
if (HAL_OPAMP_DeInit(&hopamp1) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
|
||||
level = std::max(0, level);
|
||||
level = std::min(4, level);
|
||||
|
||||
// Adjust configuration and, if PGA, gain.
|
||||
switch (level) {
|
||||
case 0: // 0dB
|
||||
hopamp1.Init.Mode = OPAMP_FOLLOWER_MODE;
|
||||
|
@ -70,20 +67,18 @@ void set_input_gain(int level)
|
|||
dc_offset = 128;
|
||||
break;
|
||||
default:
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
}
|
||||
|
||||
// Adjust DC offset. It is different for each gain setting.
|
||||
if (HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, dc_offset) != HAL_OK)
|
||||
Error_Handler();
|
||||
CxxErrorHandler();
|
||||
|
||||
// Init and start the op amp after the change.
|
||||
if (HAL_OPAMP_Init(&hopamp1) != HAL_OK)
|
||||
Error_Handler();
|
||||
|
||||
CxxErrorHandler();
|
||||
if (HAL_OPAMP_Start(&hopamp1)!= HAL_OK)
|
||||
Error_Handler();
|
||||
|
||||
// setAudioPins();
|
||||
osDelay(300);
|
||||
CxxErrorHandler();
|
||||
}
|
||||
|
||||
int adjust_input_gain() __attribute__((noinline));
|
||||
|
@ -93,15 +88,17 @@ int adjust_input_gain() {
|
|||
INFO("\nAdjusting input gain...\n");
|
||||
|
||||
int gain{0};
|
||||
uint16_t vpp, vavg, vmin, vmax;
|
||||
|
||||
set_input_gain(gain);
|
||||
auto [vpp, vavg, vmin, vmax] = readLevels(AUDIO_IN);
|
||||
osDelay(100); // Need time for DC offset to settle.
|
||||
|
||||
std::tie(vpp, vavg, vmin, vmax) = readLevels(AUDIO_IN);
|
||||
INFO("\nVpp = %" PRIu16 ", Vavg = %" PRIu16 "\n", vpp, vavg);
|
||||
INFO("\nVmin = %" PRIu16 ", Vmax = %" PRIu16 ", setting = %d\n", vmin, vmax, gain);
|
||||
|
||||
while (gain == 0 and (vmax == vref or vmin == 0))
|
||||
{
|
||||
gpio::LED_OTHER::toggle();
|
||||
std::tie(vpp, vavg, vmin, vmax) = readLevels(AUDIO_IN);
|
||||
|
||||
INFO("\nVpp = %" PRIu16 ", Vavg = %" PRIu16 "\n", vpp, vavg);
|
||||
|
@ -117,6 +114,8 @@ int adjust_input_gain() {
|
|||
else gain = 0;
|
||||
|
||||
set_input_gain(gain);
|
||||
osDelay(100); // Need time for DC offset to settle.
|
||||
|
||||
std::tie(vpp, vavg, vmin, vmax) = readLevels(AUDIO_IN);
|
||||
INFO("\nVpp = %" PRIu16 ", Vavg = %" PRIu16 "\n", vpp, vavg);
|
||||
INFO("\nVmin = %" PRIu16 ", Vmax = %" PRIu16 ", setting = %d\n", vmin, vmax, gain);
|
||||
|
@ -124,17 +123,12 @@ int adjust_input_gain() {
|
|||
virtual_ground = vavg;
|
||||
i_vgnd = 1.0 / virtual_ground;
|
||||
|
||||
gpio::LED_OTHER::on();
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
||||
|
||||
void autoAudioInputLevel()
|
||||
{
|
||||
|
||||
led_dcd_on();
|
||||
led_tx_on();
|
||||
|
||||
INFO("autoInputLevel");
|
||||
|
||||
mobilinkd::tnc::kiss::settings().input_gain = adjust_input_gain();
|
||||
|
@ -144,25 +138,26 @@ void autoAudioInputLevel()
|
|||
else if (rx_twist > 9) rx_twist = 9;
|
||||
INFO("TWIST = %ddB", rx_twist);
|
||||
mobilinkd::tnc::kiss::settings().rx_twist = rx_twist;
|
||||
mobilinkd::tnc::kiss::settings().announce_input_settings();
|
||||
mobilinkd::tnc::kiss::settings().update_crc();
|
||||
|
||||
mobilinkd::tnc::kiss::settings().store();
|
||||
|
||||
led_tx_off();
|
||||
led_dcd_off();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the audio input levels from the values stored in EEPROM.
|
||||
* Set the audio input levels from the values stored in EEPROM. Then
|
||||
* analyze the input levels to set the VGND level.
|
||||
*/
|
||||
void setAudioInputLevels()
|
||||
{
|
||||
// setAudioPins();
|
||||
INFO("Setting input gain: %d", kiss::settings().input_gain);
|
||||
set_input_gain(kiss::settings().input_gain);
|
||||
auto [vpp, vavg, vmin, vmax] = readLevels(AUDIO_IN);
|
||||
|
||||
uint16_t vpp, vavg, vmin, vmax;
|
||||
|
||||
std::tie(vpp, vavg, vmin, vmax) = readLevels(AUDIO_IN);
|
||||
INFO("Vpp = %" PRIu16 ", Vavg = %" PRIu16, vpp, vavg);
|
||||
INFO("Vmin = %" PRIu16 ", Vmax = %" PRIu16 ", setting = %d", vmin, vmax, 2);
|
||||
INFO("Vmin = %" PRIu16 ", Vmax = %" PRIu16, vmin, vmax);
|
||||
virtual_ground = vavg;
|
||||
i_vgnd = 1.0 / virtual_ground;
|
||||
}
|
||||
|
@ -194,7 +189,8 @@ std::tuple<int16_t, int16_t> computeLogAudioLevel(int16_t level)
|
|||
*/
|
||||
void setAudioOutputLevel()
|
||||
{
|
||||
auto [l, r] = computeLogAudioLevel(kiss::settings().output_gain);
|
||||
uint16_t l,r;
|
||||
std::tie(l, r) = computeLogAudioLevel(kiss::settings().output_gain);
|
||||
|
||||
INFO("Setting output gain: %" PRIi16 " (log %" PRIi16 " + %" PRIi16 ")", kiss::settings().output_gain, l, r);
|
||||
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// Copyright 2017 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "DigitalPLL.hpp"
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
namespace pll {
|
||||
|
||||
// Loop low-pass filter taps (64Hz Bessel)
|
||||
float loop_b[] = {
|
||||
0.144668495309,
|
||||
0.144668495309,
|
||||
};
|
||||
float loop_a[] = {
|
||||
1.0,
|
||||
-0.710663009381,
|
||||
};
|
||||
|
||||
// Lock low-pass filter taps (40Hz Bessel)
|
||||
float lock_b[] = {
|
||||
0.0951079834025,
|
||||
0.0951079834025,
|
||||
};
|
||||
float lock_a[] = {
|
||||
1.0,
|
||||
-0.809784033195,
|
||||
};
|
||||
|
||||
} // pll
|
||||
|
||||
}} // mobilinkd::tnc
|
|
@ -3,24 +3,20 @@
|
|||
|
||||
#include "Hysteresis.hpp"
|
||||
#include "IirFilter.hpp"
|
||||
#include "FirFilter.hpp"
|
||||
|
||||
#include "arm_math.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
namespace pll {
|
||||
|
||||
// Loop low-pass filter taps (64Hz Bessel)
|
||||
extern float loop_b[2];
|
||||
extern float loop_a[2];
|
||||
// Lock low-pass filter taps (40Hz Bessel)
|
||||
extern float lock_b[2];
|
||||
extern float lock_a[2];
|
||||
|
||||
template <typename FloatType>
|
||||
struct PLLResult {
|
||||
|
@ -29,13 +25,32 @@ struct PLLResult {
|
|||
bool locked;
|
||||
};
|
||||
|
||||
// Lock low-pass filter taps (80Hz Bessel)
|
||||
// scipy.signal:
|
||||
// b, a = bessel(4, [80.0/(1200/2)], 'lowpass')
|
||||
//
|
||||
const std::array<float, 5> lock_b = {
|
||||
1.077063e-03,4.308253e-03,6.462379e-03,4.308253e-03,1.077063e-03,
|
||||
};
|
||||
const std::array<float, 5> lock_a = {
|
||||
1.000000e+00,-2.774567e+00,2.962960e+00,-1.437990e+00,2.668296e-01,
|
||||
};
|
||||
|
||||
// 64 Hz loop filter.
|
||||
// scipy.signal:
|
||||
// loop_coeffs = firwin(9, [64.0/(1200/2)], width = None,
|
||||
// pass_zero = True, scale = True, window='hann')
|
||||
//
|
||||
const std::array<float, 7> loop_coeffs = {
|
||||
// 0.08160962754214955, 0.25029850550446403, 0.3361837339067726, 0.2502985055044641, 0.08160962754214969
|
||||
3.196252e-02,1.204223e-01,2.176819e-01,2.598666e-01,2.176819e-01,1.204223e-01,3.196252e-02
|
||||
};
|
||||
|
||||
} // pll
|
||||
|
||||
template <typename T>
|
||||
struct BaseDigitalPLL {
|
||||
|
||||
static const size_t N = 16;
|
||||
|
||||
struct BaseDigitalPLL
|
||||
{
|
||||
typedef T float_type;
|
||||
typedef pll::PLLResult<float_type> result_type;
|
||||
|
||||
|
@ -44,9 +59,9 @@ struct BaseDigitalPLL {
|
|||
float_type sps_; ///< Samples per symbol
|
||||
float_type limit_; ///< Samples per symbol / 2
|
||||
libafsk::BaseHysteresis<float_type> lock_;
|
||||
IirFilter<2> loop_filter_;
|
||||
IirFilter<2> lock_filter_;
|
||||
|
||||
FirFilter<1, 7> loop_filter_{pll::loop_coeffs.begin()};
|
||||
IirFilter<5> lock_filter_{pll::lock_b, pll::lock_a};
|
||||
|
||||
bool last_;
|
||||
float_type count_;
|
||||
|
||||
|
@ -57,20 +72,17 @@ struct BaseDigitalPLL {
|
|||
BaseDigitalPLL(float_type sample_rate, float_type symbol_rate)
|
||||
: sample_rate_(sample_rate), symbol_rate_(symbol_rate)
|
||||
, sps_(sample_rate / symbol_rate)
|
||||
, limit_(sps_ / 2.0f)
|
||||
, lock_(sps_ * 0.025, sps_ * .15, 1, 0)
|
||||
, loop_filter_(pll::loop_b, pll::loop_a)
|
||||
, lock_filter_(pll::lock_b, pll::lock_a)
|
||||
, limit_(sps_ / float_type(2.0))
|
||||
, lock_(sps_ * float_type(0.03), sps_ * float_type(0.15), 1, 0)
|
||||
, last_(false), count_(0), sample_(false)
|
||||
, jitter_(0.0), bits_(1)
|
||||
{}
|
||||
|
||||
result_type operator()(bool input)
|
||||
{
|
||||
|
||||
sample_ = false;
|
||||
|
||||
if (input != last_ or bits_ > 127) {
|
||||
if (input != last_ or bits_ > 16) {
|
||||
// Record transition.
|
||||
last_ = input;
|
||||
|
||||
|
@ -78,11 +90,15 @@ struct BaseDigitalPLL {
|
|||
count_ -= sps_;
|
||||
}
|
||||
|
||||
float_type offset = count_ / bits_;
|
||||
float_type jitter = loop_filter_(offset);
|
||||
jitter_ = lock_filter_(abs(offset));
|
||||
// Force lock off when no stimulus is present (squelch closed).
|
||||
const float_type adjust = bits_ > 16 ? 5.0 : 0.0;
|
||||
|
||||
count_ -= jitter * sps_ * 0.023f;
|
||||
const float_type offset = count_ / bits_;
|
||||
const float_type jitter = loop_filter_(offset);
|
||||
const float_type abs_offset = std::abs(offset) + adjust;
|
||||
jitter_ = lock_filter_(abs_offset);
|
||||
|
||||
count_ -= jitter / 2;
|
||||
|
||||
bits_ = 1;
|
||||
} else {
|
||||
|
|
460
TNC/Filter.cpp
460
TNC/Filter.cpp
|
@ -1,460 +0,0 @@
|
|||
#include "Filter.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
const float filter_taps[13][FILTER_TAP_NUM] = {
|
||||
{
|
||||
// 0dB Hamming (1693.3)
|
||||
0.0092140576627,
|
||||
0.00768806806252,
|
||||
0.00498340794238,
|
||||
-0.00171692105003,
|
||||
-0.0142537522489,
|
||||
-0.0319314833665,
|
||||
-0.0509364246735,
|
||||
-0.0650918044189,
|
||||
-0.0677825846114,
|
||||
-0.0544018818,
|
||||
-0.0244193735639,
|
||||
0.0177467007765,
|
||||
0.0634791554618,
|
||||
0.102191070678,
|
||||
0.124356477862,
|
||||
0.124356477862,
|
||||
0.102191070678,
|
||||
0.0634791554618,
|
||||
0.0177467007765,
|
||||
-0.0244193735639,
|
||||
-0.0544018818,
|
||||
-0.0677825846114,
|
||||
-0.0650918044189,
|
||||
-0.0509364246735,
|
||||
-0.0319314833665,
|
||||
-0.0142537522489,
|
||||
-0.00171692105003,
|
||||
0.00498340794238,
|
||||
0.00768806806252,
|
||||
0.0092140576627,
|
||||
},
|
||||
{
|
||||
// 1dB Hamming (1749.2)
|
||||
0.00984360063296,
|
||||
0.00907660156831,
|
||||
0.00733802154094,
|
||||
0.00165885599255,
|
||||
-0.01027803496,
|
||||
-0.028288428514,
|
||||
-0.0487844743735,
|
||||
-0.0653441331879,
|
||||
-0.0706814843758,
|
||||
-0.0593532384181,
|
||||
-0.0301832127721,
|
||||
0.0125781304703,
|
||||
0.0599187881831,
|
||||
0.100456125433,
|
||||
0.123807723158,
|
||||
0.123807723158,
|
||||
0.100456125433,
|
||||
0.0599187881831,
|
||||
0.0125781304703,
|
||||
-0.0301832127721,
|
||||
-0.0593532384181,
|
||||
-0.0706814843758,
|
||||
-0.0653441331879,
|
||||
-0.0487844743735,
|
||||
-0.028288428514,
|
||||
-0.01027803496,
|
||||
0.00165885599255,
|
||||
0.00733802154094,
|
||||
0.00907660156831,
|
||||
0.00984360063296,
|
||||
},
|
||||
{
|
||||
// 2dB Hamming (1805.6)
|
||||
0.0101071938454,
|
||||
0.0101751907488,
|
||||
0.00949571140771,
|
||||
0.00500536790767,
|
||||
-0.00608985117047,
|
||||
-0.0241824665512,
|
||||
-0.0460028160886,
|
||||
-0.0649487308381,
|
||||
-0.0730641303456,
|
||||
-0.0640057046058,
|
||||
-0.0358585166096,
|
||||
0.00736846731409,
|
||||
0.0562886910258,
|
||||
0.098688560769,
|
||||
0.12326772148,
|
||||
0.12326772148,
|
||||
0.098688560769,
|
||||
0.0562886910258,
|
||||
0.00736846731409,
|
||||
-0.0358585166096,
|
||||
-0.0640057046058,
|
||||
-0.0730641303456,
|
||||
-0.0649487308381,
|
||||
-0.0460028160886,
|
||||
-0.0241824665512,
|
||||
-0.00608985117047,
|
||||
0.00500536790767,
|
||||
0.00949571140771,
|
||||
0.0101751907488,
|
||||
0.0101071938454,
|
||||
},
|
||||
{
|
||||
// 3dB Hamming (1861.5)
|
||||
0.00999732596792,
|
||||
0.0109364331138,
|
||||
0.0113663405297,
|
||||
0.00819091080479,
|
||||
-0.00184492788726,
|
||||
-0.0197616766146,
|
||||
-0.0426984518018,
|
||||
-0.0639498932619,
|
||||
-0.0749094986595,
|
||||
-0.0682876192213,
|
||||
-0.0413467222516,
|
||||
0.00222026614421,
|
||||
0.0526805390514,
|
||||
0.0969649735461,
|
||||
0.1228029701,
|
||||
0.1228029701,
|
||||
0.0969649735461,
|
||||
0.0526805390514,
|
||||
0.00222026614421,
|
||||
-0.0413467222516,
|
||||
-0.0682876192213,
|
||||
-0.0749094986595,
|
||||
-0.0639498932619,
|
||||
-0.0426984518018,
|
||||
-0.0197616766146,
|
||||
-0.00184492788726,
|
||||
0.00819091080479,
|
||||
0.0113663405297,
|
||||
0.0109364331138,
|
||||
0.00999732596792,
|
||||
},
|
||||
{
|
||||
// 4dB Hamming (1917.0)
|
||||
0.00953170942079,
|
||||
0.0113523463692,
|
||||
0.012917947221,
|
||||
0.0111611200433,
|
||||
0.00238829862135,
|
||||
-0.0150956296368,
|
||||
-0.0389313364275,
|
||||
-0.0623942365343,
|
||||
-0.07625339099,
|
||||
-0.0722276074899,
|
||||
-0.0466690276902,
|
||||
-0.00287524097904,
|
||||
0.0491038547992,
|
||||
0.095314261046,
|
||||
0.122454987694,
|
||||
0.122454987694,
|
||||
0.095314261046,
|
||||
0.0491038547992,
|
||||
-0.00287524097904,
|
||||
-0.0466690276902,
|
||||
-0.0722276074899,
|
||||
-0.07625339099,
|
||||
-0.0623942365343,
|
||||
-0.0389313364275,
|
||||
-0.0150956296368,
|
||||
0.00238829862135,
|
||||
0.0111611200433,
|
||||
0.012917947221,
|
||||
0.0113523463692,
|
||||
0.00953170942079,
|
||||
},
|
||||
{
|
||||
// 5dB Hamming (1972.5)
|
||||
0.00872938144139,
|
||||
0.011420928511,
|
||||
0.0141310255503,
|
||||
0.0138829549523,
|
||||
0.00657217070647,
|
||||
-0.0102167866884,
|
||||
-0.0347251133165,
|
||||
-0.0603020097307,
|
||||
-0.0771226785494,
|
||||
-0.0758651302103,
|
||||
-0.0518731312861,
|
||||
-0.00796039989017,
|
||||
0.0455363026357,
|
||||
0.0937399215189,
|
||||
0.122245216908,
|
||||
0.122245216908,
|
||||
0.0937399215189,
|
||||
0.0455363026357,
|
||||
-0.00796039989017,
|
||||
-0.0518731312861,
|
||||
-0.0758651302103,
|
||||
-0.0771226785494,
|
||||
-0.0603020097307,
|
||||
-0.0347251133165,
|
||||
-0.0102167866884,
|
||||
0.00657217070647,
|
||||
0.0138829549523,
|
||||
0.0141310255503,
|
||||
0.011420928511,
|
||||
0.00872938144139,
|
||||
},
|
||||
{
|
||||
// 6dB Hamming (2027.0)
|
||||
0.00763984171566,
|
||||
0.0111488308293,
|
||||
0.0149655370284,
|
||||
0.0162631703876,
|
||||
0.0105693777806,
|
||||
-0.00527673382432,
|
||||
-0.0302054840484,
|
||||
-0.0577401237325,
|
||||
-0.077513860058,
|
||||
-0.079139201575,
|
||||
-0.0568701896276,
|
||||
-0.0129510233888,
|
||||
0.0420349410452,
|
||||
0.0922665002,
|
||||
0.122176947883,
|
||||
0.122176947883,
|
||||
0.0922665002,
|
||||
0.0420349410452,
|
||||
-0.0129510233888,
|
||||
-0.0568701896276,
|
||||
-0.079139201575,
|
||||
-0.077513860058,
|
||||
-0.0577401237325,
|
||||
-0.0302054840484,
|
||||
-0.00527673382432,
|
||||
0.0105693777806,
|
||||
0.0162631703876,
|
||||
0.0149655370284,
|
||||
0.0111488308293,
|
||||
0.00763984171566,
|
||||
},
|
||||
{
|
||||
// 7dB Cosine (1986.0)
|
||||
0.00460770532874,
|
||||
0.0162873765157,
|
||||
0.0251984580718,
|
||||
0.0248176615108,
|
||||
0.0117845748105,
|
||||
-0.0125423969002,
|
||||
-0.0421426113528,
|
||||
-0.0680353327511,
|
||||
-0.0811090126152,
|
||||
-0.0751714219734,
|
||||
-0.0492188545506,
|
||||
-0.00815365116672,
|
||||
0.038335087686,
|
||||
0.0784276419333,
|
||||
0.101570441881,
|
||||
0.101570441881,
|
||||
0.0784276419333,
|
||||
0.038335087686,
|
||||
-0.00815365116672,
|
||||
-0.0492188545506,
|
||||
-0.0751714219734,
|
||||
-0.0811090126152,
|
||||
-0.0680353327511,
|
||||
-0.0421426113528,
|
||||
-0.0125423969002,
|
||||
0.0117845748105,
|
||||
0.0248176615108,
|
||||
0.0251984580718,
|
||||
0.0162873765157,
|
||||
0.00460770532874,
|
||||
},
|
||||
{
|
||||
// 8dB Cosine (2021.0, 2021.1)
|
||||
0.00420597097315,
|
||||
0.0159601020805,
|
||||
0.0260190570929,
|
||||
0.0273151975444,
|
||||
0.0157146317097,
|
||||
-0.00808663238039,
|
||||
-0.0383509470879,
|
||||
-0.0659043862717,
|
||||
-0.0810670338405,
|
||||
-0.0769492259504,
|
||||
-0.0520051674945,
|
||||
-0.0109566710433,
|
||||
0.0362780863637,
|
||||
0.0773640302855,
|
||||
0.101184010968,
|
||||
0.101184010968,
|
||||
0.0773640302855,
|
||||
0.0362780863637,
|
||||
-0.0109566710433,
|
||||
-0.0520051674945,
|
||||
-0.0769492259504,
|
||||
-0.0810670338405,
|
||||
-0.0659043862717,
|
||||
-0.0383509470879,
|
||||
-0.00808663238039,
|
||||
0.0157146317097,
|
||||
0.0273151975444,
|
||||
0.0260190570929,
|
||||
0.0159601020805,
|
||||
0.00420597097315,
|
||||
|
||||
},
|
||||
{
|
||||
// 9dB Hamming (2186.5)
|
||||
0.00308518474955,
|
||||
0.00850291315071,
|
||||
0.0151934174423,
|
||||
0.0211164164746,
|
||||
0.0210280537958,
|
||||
0.00946135065651,
|
||||
-0.0151061442669,
|
||||
-0.0474011160773,
|
||||
-0.0758516294354,
|
||||
-0.0867915071262,
|
||||
-0.0707058729109,
|
||||
-0.0275514181864,
|
||||
0.0316531744803,
|
||||
0.0881474710462,
|
||||
0.122515729369,
|
||||
0.122515729369,
|
||||
0.0881474710462,
|
||||
0.0316531744803,
|
||||
-0.0275514181864,
|
||||
-0.0707058729109,
|
||||
-0.0867915071262,
|
||||
-0.0758516294354,
|
||||
-0.0474011160773,
|
||||
-0.0151061442669,
|
||||
0.00946135065651,
|
||||
0.0210280537958,
|
||||
0.0211164164746,
|
||||
0.0151934174423,
|
||||
0.00850291315071,
|
||||
0.00308518474955,
|
||||
},
|
||||
{
|
||||
// 10dB Cosine (2087.0, 2087.1)
|
||||
0.003297704105,
|
||||
0.0148243949961,
|
||||
0.0268172892254,
|
||||
0.0313287864329,
|
||||
0.0227440831014,
|
||||
0.00036329489673,
|
||||
-0.0307896841323,
|
||||
-0.061300901507,
|
||||
-0.0804397189579,
|
||||
-0.0799281940065,
|
||||
-0.0570830747612,
|
||||
-0.0161917885552,
|
||||
0.0324263666501,
|
||||
0.0754301963068,
|
||||
0.100574174183,
|
||||
0.100574174183,
|
||||
0.0754301963068,
|
||||
0.0324263666501,
|
||||
-0.0161917885552,
|
||||
-0.0570830747612,
|
||||
-0.0799281940065,
|
||||
-0.0804397189579,
|
||||
-0.061300901507,
|
||||
-0.0307896841323,
|
||||
0.00036329489673,
|
||||
0.0227440831014,
|
||||
0.0313287864329,
|
||||
0.0268172892254,
|
||||
0.0148243949961,
|
||||
0.003297704105,
|
||||
},
|
||||
{
|
||||
// 11dB Cosine (2119.0, 2119.1)
|
||||
0.00279757679371,
|
||||
0.0140443163212,
|
||||
0.0268514195956,
|
||||
0.0329271588621,
|
||||
0.0259396342176,
|
||||
0.00444730813715,
|
||||
-0.0269594711541,
|
||||
-0.0588146601004,
|
||||
-0.0798925711826,
|
||||
-0.0812085944654,
|
||||
-0.0594715656311,
|
||||
-0.01871136648,
|
||||
0.0305735278948,
|
||||
0.0745360339334,
|
||||
0.100349256713,
|
||||
0.100349256713,
|
||||
0.0745360339334,
|
||||
0.0305735278948,
|
||||
-0.01871136648,
|
||||
-0.0594715656311,
|
||||
-0.0812085944654,
|
||||
-0.0798925711826,
|
||||
-0.0588146601004,
|
||||
-0.0269594711541,
|
||||
0.00444730813715,
|
||||
0.0259396342176,
|
||||
0.0329271588621,
|
||||
0.0268514195956,
|
||||
0.0140443163212,
|
||||
0.00279757679371,
|
||||
},
|
||||
{
|
||||
// 12dB Cosine (2150,2151)
|
||||
0.0022752196356,
|
||||
0.0131417834537,
|
||||
0.0266616639804,
|
||||
0.0342658094422,
|
||||
0.028925742996,
|
||||
0.00843279814311,
|
||||
-0.0231061648363,
|
||||
-0.0562188716332,
|
||||
-0.0792056020955,
|
||||
-0.0823667306435,
|
||||
-0.0617763153975,
|
||||
-0.0211791883525,
|
||||
0.0287608072456,
|
||||
0.0736871278906,
|
||||
0.100177853829,
|
||||
0.100177853829,
|
||||
0.0736871278906,
|
||||
0.0287608072456,
|
||||
-0.0211791883525,
|
||||
-0.0617763153975,
|
||||
-0.0823667306435,
|
||||
-0.0792056020955,
|
||||
-0.0562188716332,
|
||||
-0.0231061648363,
|
||||
0.00843279814311,
|
||||
0.028925742996,
|
||||
0.0342658094422,
|
||||
0.0266616639804,
|
||||
0.0131417834537,
|
||||
0.0022752196356,
|
||||
}
|
||||
};
|
||||
|
||||
void Filter_init(Filter* f, const float* taps) {
|
||||
for (size_t i = 0; i != FILTER_TAP_NUM; ++i) {
|
||||
f->history[i] = 0;
|
||||
f->taps[i] = taps[i]; // initialize ccmram.
|
||||
}
|
||||
f->last_index = 0;
|
||||
}
|
||||
|
||||
void Filter_put(Filter* f, float input) {
|
||||
f->history[f->last_index++] = input;
|
||||
if (f->last_index == FILTER_TAP_NUM)
|
||||
f->last_index = 0;
|
||||
}
|
||||
|
||||
float Filter_get(Filter* f) {
|
||||
float acc = 0;
|
||||
int index = f->last_index, i;
|
||||
for (i = 0; i < FILTER_TAP_NUM; ++i) {
|
||||
index = index != 0 ? index - 1 : FILTER_TAP_NUM - 1;
|
||||
acc += f->history[index] * f->taps[i];
|
||||
};
|
||||
return acc;
|
||||
}
|
42
TNC/Filter.h
42
TNC/Filter.h
|
@ -1,42 +0,0 @@
|
|||
#ifndef FILTER_H_
|
||||
#define FILTER_H_
|
||||
|
||||
/*
|
||||
|
||||
FIR filter designed with
|
||||
http://t-filter.appspot.com
|
||||
|
||||
sampling frequency: 20000 Hz
|
||||
|
||||
* 0 Hz - 800 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -40.37597293671087 dB
|
||||
|
||||
* 1100 Hz - 2300 Hz
|
||||
gain = 1
|
||||
desired ripple = 2 dB
|
||||
actual ripple = 1.474114871585135 dB
|
||||
|
||||
* 2600 Hz - 10000 Hz
|
||||
gain = 0
|
||||
desired attenuation = -40 dB
|
||||
actual attenuation = -40.37597293671087 dB
|
||||
|
||||
*/
|
||||
|
||||
#define FILTER_TAP_NUM 31
|
||||
|
||||
extern const float filter_taps[13][FILTER_TAP_NUM];
|
||||
|
||||
typedef struct {
|
||||
float taps[FILTER_TAP_NUM];
|
||||
float history[FILTER_TAP_NUM];
|
||||
unsigned int last_index;
|
||||
} Filter;
|
||||
|
||||
void Filter_init(Filter* f, const float* taps);
|
||||
void Filter_put(Filter* f, float input);
|
||||
float Filter_get(Filter* f);
|
||||
|
||||
#endif
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2015 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__TNC__FILTER_H_
|
||||
#define MOBILINKD__TNC__FILTER_H_
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
struct Filter {
|
||||
virtual float operator()(float) = 0;
|
||||
virtual ~Filter() {}
|
||||
};
|
||||
|
||||
}} // mobilinkd::tnc
|
||||
|
||||
#endif // MOBILINKD__TNC__FILTER_H_
|
|
@ -9,28 +9,6 @@
|
|||
|
||||
namespace mobilinkd { namespace tnc { namespace filter {
|
||||
|
||||
const TIirCoefficients<2> dB_3 = {
|
||||
{0.148416305668, 0.148416305668},
|
||||
{1.0, -0.703167388663}
|
||||
};
|
||||
|
||||
const TIirCoefficients<2> dB0 = {{1.0, 1.0}, {1.0, 1.0}};
|
||||
|
||||
const TIirCoefficients<2> dB3 = {
|
||||
{0.821330488584, -0.821330488584},
|
||||
{1.0, -0.642660977168}
|
||||
};
|
||||
|
||||
const TIirCoefficients<3> dB6 = {
|
||||
{0.704863714497, -1.40972742899, 0.704863714497},
|
||||
{1.0, -1.3445352861, 0.474919571889}
|
||||
};
|
||||
|
||||
const TIirCoefficients<4> dB9= {
|
||||
{0.605034079053, -1.81510223716, 1.81510223716, -0.605034079053},
|
||||
{1.0, -2.04985913806, 1.44420376126, -0.346209733102}
|
||||
};
|
||||
|
||||
namespace fir {
|
||||
|
||||
// 1200Hz = -12dB, 2200Hz = 0dB; 3653Hz cutoff, 4.93 gain; cosine.
|
||||
|
@ -341,65 +319,6 @@ const TFirCoefficients<9>* AfskFilters[] = {
|
|||
&dB12
|
||||
};
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
const TFirCoefficients<9> dB_3 = {
|
||||
{ 0.0190268296698,
|
||||
0.0720836132753,
|
||||
0.131839958497,
|
||||
0.178778602372,
|
||||
0.196541992372,
|
||||
0.178778602372,
|
||||
0.131839958497,
|
||||
0.0720836132753,
|
||||
0.0190268296698,
|
||||
},
|
||||
};
|
||||
|
||||
const TFirCoefficients<9> dB3 = {
|
||||
{
|
||||
-0.0137747043237,
|
||||
-0.0496069821386,
|
||||
-0.0880620689921,
|
||||
-0.11751320055,
|
||||
0.869433181254,
|
||||
-0.11751320055,
|
||||
-0.0880620689921,
|
||||
-0.0496069821386,
|
||||
-0.0137747043237,
|
||||
}
|
||||
};
|
||||
|
||||
const TFirCoefficients<9> dB6 = {
|
||||
{
|
||||
-0.00808574330071,
|
||||
-0.0502334418895,
|
||||
-0.115447610972,
|
||||
-0.175051834084,
|
||||
0.796496156597,
|
||||
-0.175051834084,
|
||||
-0.115447610972,
|
||||
-0.0502334418895,
|
||||
-0.00808574330071,
|
||||
}
|
||||
};
|
||||
|
||||
const TFirCoefficients<9> dB9= {
|
||||
{ -0.000653382869306,
|
||||
-0.0386591907088,
|
||||
-0.121362869,
|
||||
-0.208082569133,
|
||||
0.750548984054,
|
||||
-0.208082569133,
|
||||
-0.121362869,
|
||||
-0.0386591907088,
|
||||
-0.000653382869306,
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
} // fir
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// Copyright 2015 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__TNC__FIR_FILTER_H_
|
||||
#define MOBILINKD__TNC__FIR_FILTER_H_
|
||||
|
||||
#include "Filter.hpp"
|
||||
#include <AudioLevel.hpp>
|
||||
|
||||
#include "arm_math.h"
|
||||
|
@ -74,7 +73,7 @@ struct FirFilter {
|
|||
float* operator()(int16_t* input) // __attribute__((section(".bss2")))
|
||||
{
|
||||
for (size_t i = 0; i != BLOCK_SIZE; i++) {
|
||||
filter_input[i] = (float(input[i]) - vgnd_) * audio::i_vgnd;
|
||||
filter_input[i] = float(input[i]);
|
||||
}
|
||||
arm_fir_f32(&instance, filter_input, filter_output, BLOCK_SIZE);
|
||||
return filter_output;
|
||||
|
@ -85,6 +84,54 @@ struct FirFilter {
|
|||
arm_fir_f32(&instance, input, filter_output, BLOCK_SIZE);
|
||||
return filter_output;
|
||||
}
|
||||
|
||||
float operator()(float input) // __attribute__((section(".bss2")))
|
||||
{
|
||||
arm_fir_f32(&instance, &input, filter_output, 1);
|
||||
return *filter_output;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t BLOCK_SIZE, size_t FILTER_SIZE>
|
||||
struct Q15FirFilter {
|
||||
const q15_t* filter_taps{nullptr};
|
||||
q15_t filter_state[BLOCK_SIZE + FILTER_SIZE - 1];
|
||||
q15_t filter_input[BLOCK_SIZE];
|
||||
q15_t filter_output[BLOCK_SIZE];
|
||||
q15_t vgnd_{0};
|
||||
q15_t i_vgnd_{0};
|
||||
arm_fir_instance_q15 instance{};
|
||||
|
||||
Q15FirFilter()
|
||||
{}
|
||||
|
||||
Q15FirFilter(const q15_t* taps)
|
||||
: filter_taps(taps)
|
||||
{
|
||||
init(taps);
|
||||
}
|
||||
|
||||
void init(const q15_t* taps)
|
||||
{
|
||||
vgnd_ = audio::virtual_ground;
|
||||
filter_taps = taps;
|
||||
arm_fir_init_q15(&instance, FILTER_SIZE, const_cast<q15_t*>(filter_taps), // WTF ARM?!?
|
||||
filter_state, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// ADC input
|
||||
q15_t* operator()(q15_t* input) // __attribute__((section(".bss2")))
|
||||
{
|
||||
arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE);
|
||||
return filter_output;
|
||||
}
|
||||
|
||||
// LPF input
|
||||
q15_t* filter(q15_t* input) // __attribute__((section(".bss2")))
|
||||
{
|
||||
arm_fir_fast_q15(&instance, input, filter_output, BLOCK_SIZE);
|
||||
return filter_output;
|
||||
}
|
||||
};
|
||||
|
||||
}} // mobilinkd::tnc
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <arm_math.h>
|
||||
#include <complex>
|
||||
#include <cmath>
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ using namespace mobilinkd::libafsk;
|
|||
|
||||
struct Encoder {
|
||||
|
||||
static const uint8_t IDLE = 0x00;
|
||||
static const uint8_t FLAG = 0x7E;
|
||||
|
||||
enum class state_type {
|
||||
|
@ -71,6 +72,7 @@ struct Encoder {
|
|||
// See if we have back-to-back frames.
|
||||
evt = osMessagePeek(input_, 0);
|
||||
if (evt.status != osEventMessage) {
|
||||
send_raw(IDLE);
|
||||
send_delay_ = true;
|
||||
if (!duplex_) {
|
||||
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
|
||||
|
@ -117,6 +119,8 @@ struct Encoder {
|
|||
* For APRS digipeaters, the slot_time and p values should be 0 and 255,
|
||||
* respectively. This is equivalent to 1-persistent CSMA.
|
||||
*
|
||||
* @pre The demodulator is running in order to detect the data carrier.
|
||||
*
|
||||
* @note For this to work, the demodulator must be left running
|
||||
* while CSMA is taking place in order to do carrier detection.
|
||||
*
|
||||
|
@ -125,8 +129,7 @@ struct Encoder {
|
|||
*/
|
||||
bool do_csma() {
|
||||
// Wait until we can transmit. If we cannot transmit for 10s
|
||||
// drop the frame. Note that we cheat a bit by looking at the
|
||||
// state of the DCD_LED to determine if the channel is clear.
|
||||
// drop the frame.
|
||||
|
||||
if (!led_dcd_status()) {
|
||||
// Channel is clear... send now.
|
||||
|
@ -134,8 +137,8 @@ struct Encoder {
|
|||
}
|
||||
|
||||
uint16_t counter = 0;
|
||||
while (counter < 10000) {
|
||||
osDelay(slot_time_); // We count on minimum delay = 1.
|
||||
while (counter < 1000) {
|
||||
osDelay(slot_time_ * 10); // We count on minimum delay = 1.
|
||||
counter += slot_time_;
|
||||
|
||||
if (rng_() < p_persist_) {
|
||||
|
@ -160,6 +163,10 @@ struct Encoder {
|
|||
* flag is not cleared. This will cause CSMA and TX delay to be
|
||||
* attempted on the next frame.
|
||||
*
|
||||
* @pre either send_delay_ is false or the demodulator is running. We
|
||||
* expect that send_delay_ is false only when we have back-to-back
|
||||
* packets.
|
||||
*
|
||||
* @param frame
|
||||
*/
|
||||
void process(IoFrame* frame) {
|
||||
|
@ -168,7 +175,10 @@ struct Encoder {
|
|||
frame->add_fcs();
|
||||
|
||||
if (send_delay_) {
|
||||
if (not do_csma()) return;
|
||||
if (not do_csma()) {
|
||||
release(frame);
|
||||
return;
|
||||
}
|
||||
if (!duplex_) {
|
||||
osMessagePut(audioInputQueueHandle, audio::IDLE, osWaitForever);
|
||||
}
|
||||
|
@ -184,8 +194,9 @@ struct Encoder {
|
|||
void send_delay() {
|
||||
const size_t tmp = (tx_delay_ * 3) / 2;
|
||||
for (size_t i = 0; i != tmp; i++) {
|
||||
send_raw(FLAG);
|
||||
send_raw(IDLE);
|
||||
}
|
||||
send_raw(FLAG);
|
||||
}
|
||||
|
||||
void send_fcs(uint16_t fcs) {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
// Copyright 2016 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2016-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "HdlcDecoder.hpp"
|
||||
#include "GPIO.hpp"
|
||||
#include "Log.h"
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace hdlc {
|
||||
|
||||
Decoder::Decoder(bool pass_all)
|
||||
: state_(SEARCH), ones_(0), buffer_(0), frame_(ioFramePool().acquire())
|
||||
: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire())
|
||||
, bits_(0), passall_(pass_all), ready_(false)
|
||||
{}
|
||||
|
||||
|
@ -17,16 +18,16 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
|
||||
// It appears that the ioFramePool may not get initialized in the proper
|
||||
// order during some builds.
|
||||
if (nullptr == frame_) frame_ = ioFramePool().acquire();
|
||||
if (nullptr == frame_) frame_ = acquire();
|
||||
if (nullptr == frame_) return result;
|
||||
|
||||
if (not pll) {
|
||||
if ((state_ == FRAMING) and (frame_->size() > 17) and passall_) {
|
||||
if ((state_ == FRAMING) and (frame_->size() >= 15) and passall_) {
|
||||
frame_->parse_fcs();
|
||||
if (passall_ or frame_->ok()) {
|
||||
result = frame_;
|
||||
ready_ = false;
|
||||
frame_ = ioFramePool().acquire();
|
||||
frame_ = acquire();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +38,8 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
frame_->parse_fcs();
|
||||
if (passall_ or frame_->ok()) {
|
||||
result = frame_;
|
||||
frame_ = ioFramePool().acquire();
|
||||
frame_ = acquire();
|
||||
if (frame_) start_hunt();
|
||||
return result;
|
||||
}
|
||||
frame_->clear();
|
||||
|
@ -47,7 +49,119 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
return result;
|
||||
}
|
||||
|
||||
NewDecoder::optional_result_type NewDecoder::operator()(bool input, bool pll_lock)
|
||||
{
|
||||
optional_result_type result = nullptr;
|
||||
|
||||
auto status = process(input, pll_lock);
|
||||
if (status)
|
||||
{
|
||||
// INFO("Frame Status = %02x, size = %d, CRC = %04x",
|
||||
// int(status), int(packet->size()), int(packet->crc()));
|
||||
if (((status & STATUS_OK) || passall) && packet->size() > 10)
|
||||
{
|
||||
result = packet;
|
||||
packet = nullptr;
|
||||
} else {
|
||||
packet->clear();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t NewDecoder::process(bool input, bool pll_lock)
|
||||
{
|
||||
uint8_t result_code = 0;
|
||||
|
||||
while (packet == nullptr) {
|
||||
packet = ioFramePool().acquire();
|
||||
if (!packet) osThreadYield();
|
||||
}
|
||||
|
||||
if (pll_lock) {
|
||||
if (ones == 5) {
|
||||
if (input) {
|
||||
// flag byte
|
||||
flag = 1;
|
||||
} else {
|
||||
// bit stuffing...
|
||||
flag = 0;
|
||||
ones = 0;
|
||||
return result_code;
|
||||
}
|
||||
}
|
||||
|
||||
buffer >>= 1;
|
||||
buffer |= (input * 128);
|
||||
bits += 1; // Free-running until Sync byte.
|
||||
if (input) {
|
||||
++ones;
|
||||
} else {
|
||||
ones = 0;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
switch (buffer) {
|
||||
case 0x7E:
|
||||
if (packet->size()) {
|
||||
packet->parse_fcs();
|
||||
result_code = packet->ok() ? STATUS_OK : STATUS_CRC_ERROR;
|
||||
}
|
||||
state = State::SYNC;
|
||||
flag = 0;
|
||||
bits = 0;
|
||||
break;
|
||||
case 0xFE:
|
||||
if (packet->size()) {
|
||||
result_code = STATUS_FRAME_ABORT;
|
||||
}
|
||||
state = State::IDLE;
|
||||
flag = 0;
|
||||
bits = 0;
|
||||
break;
|
||||
default:
|
||||
/* pass */
|
||||
break;
|
||||
}
|
||||
return result_code;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
|
||||
case State::IDLE:
|
||||
break;
|
||||
|
||||
case State::SYNC:
|
||||
if (bits == 8) { // 8th bit.
|
||||
// Start of frame data.
|
||||
state = State::RECEIVE;
|
||||
packet->push_back(buffer);
|
||||
bits = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::RECEIVE:
|
||||
if (bits == 8) { // 8th bit.
|
||||
packet->push_back(buffer);
|
||||
bits = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PLL unlocked.
|
||||
if (packet->size()) {
|
||||
packet->parse_fcs();
|
||||
result_code = packet->ok() ? STATUS_OK | STATUS_NO_CARRIER : STATUS_NO_CARRIER;
|
||||
}
|
||||
if (state != State::IDLE) {
|
||||
buffer = 0;
|
||||
flag = 0;
|
||||
bits = 0;
|
||||
state = State::IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
return result_code;
|
||||
}
|
||||
|
||||
}}} // mobilinkd::tnc::hdlc
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD___HDLC_DECODER_HPP_
|
||||
|
@ -222,7 +222,7 @@ struct Decoder
|
|||
}
|
||||
|
||||
if (have_flag()) {
|
||||
if (frame_->size() > 17) {
|
||||
if (frame_->size() >= 15) {
|
||||
ready_ = true;
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ struct Decoder
|
|||
|
||||
// Framing error. Drop the frame. If there is a FLAG
|
||||
// in the buffer, go into HUNT otherwise SEARCH.
|
||||
if (frame_->size() > 17) {
|
||||
if (frame_->size() > 15) {
|
||||
ready_ = true;
|
||||
return;
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ struct Decoder
|
|||
#if 0
|
||||
|
||||
Decoder::Decoder(bool pass_all)
|
||||
: state_(SEARCH), ones_(0), buffer_(0), frame_(ioFramePool().acquire())
|
||||
: state_(SEARCH), ones_(0), buffer_(0), frame_(acquire())
|
||||
, bits_(0), passall_(pass_all), ready_(false)
|
||||
{}
|
||||
|
||||
|
@ -293,15 +293,15 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
|
||||
// It appears that the ioFramePool may not get initialized in the proper
|
||||
// order during some builds.
|
||||
if (nullptr == frame_) frame_ = ioFramePool().acquire();
|
||||
if (nullptr == frame_) frame_ = acquire();
|
||||
|
||||
if (not pll) {
|
||||
if ((state_ == FRAMING) and (frame_->size() > 17) and passall_) {
|
||||
if ((state_ == FRAMING) and (frame_->size() > 15) and passall_) {
|
||||
frame_->parse_fcs();
|
||||
if (passall_ or frame_->ok()) {
|
||||
result = frame_;
|
||||
ready_ = false;
|
||||
frame_ = ioFramePool().acquire();
|
||||
frame_ = acquire();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
frame_->parse_fcs();
|
||||
if (passall_ or frame_->ok()) {
|
||||
result = frame_;
|
||||
frame_ = ioFramePool().acquire();
|
||||
frame_ = acquire();
|
||||
return result;
|
||||
}
|
||||
frame_->clear();
|
||||
|
@ -323,6 +323,42 @@ IoFrame* Decoder::operator()(bool bit, bool pll)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct NewDecoder
|
||||
{
|
||||
enum class State {IDLE, SYNC, RECEIVE};
|
||||
using frame_type = IoFrame;
|
||||
using result_type = std::tuple<frame_type*, uint8_t>;
|
||||
using optional_result_type = frame_type*;
|
||||
|
||||
static constexpr uint8_t STATUS_OK{0x01};
|
||||
static constexpr uint8_t STATUS_USER_CANCEL{0x02};
|
||||
static constexpr uint8_t STATUS_FRAME_ABORT{0x04};
|
||||
static constexpr uint8_t STATUS_FRAME_ERROR{0x08};
|
||||
static constexpr uint8_t STATUS_NO_CARRIER{0x10};
|
||||
static constexpr uint8_t STATUS_CRC_ERROR{0x20};
|
||||
|
||||
const uint16_t VALID_CRC = 0xf0b8;
|
||||
|
||||
State state{State::IDLE};
|
||||
|
||||
uint8_t buffer{0};
|
||||
|
||||
uint8_t bits{0};
|
||||
uint8_t ones{0};
|
||||
bool flag{0};
|
||||
bool passall{false};
|
||||
|
||||
frame_type* packet{nullptr};
|
||||
|
||||
NewDecoder(bool pass_all=false)
|
||||
: passall(pass_all)
|
||||
{}
|
||||
|
||||
optional_result_type operator()(bool input, bool pll_lock);
|
||||
uint8_t process(bool input, bool pll_lock);
|
||||
};
|
||||
|
||||
}}} // mobilinkd::tnc::hdlc
|
||||
|
||||
#endif // MOBILINKD___HDLC_DECODER_HPP_
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// All rights reserved.
|
||||
|
||||
#include "HdlcFrame.hpp"
|
||||
#include "Log.h"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace hdlc {
|
||||
|
||||
|
@ -17,4 +19,21 @@ void release(IoFrame* frame)
|
|||
ioFramePool().release(frame);
|
||||
}
|
||||
|
||||
IoFrame* acquire()
|
||||
{
|
||||
auto result = ioFramePool().acquire();
|
||||
if (result == nullptr) CxxErrorHandler();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
IoFrame* acquire_wait()
|
||||
{
|
||||
IoFrame* result = nullptr;
|
||||
while ((result = ioFramePool().acquire()) == nullptr) {
|
||||
osThreadYield();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}}} // mobilinkd::tnc::hdlc
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ public:
|
|||
typedef typename data_type::iterator iterator;
|
||||
|
||||
enum Type {
|
||||
DATA, TX_DELAY, P_PERSIST, SLOT_TIME, TX_TAIL, DUPLEX, HARDWARE,
|
||||
DATA = 0, TX_DELAY, P_PERSIST, SLOT_TIME, TX_TAIL, DUPLEX, HARDWARE,
|
||||
TEXT, LOG};
|
||||
|
||||
enum Source {
|
||||
|
@ -72,7 +72,7 @@ private:
|
|||
checksum <<= 16; // Shift
|
||||
asm volatile("rbit %0, %0" : "+r" (checksum)); // Reverse
|
||||
uint16_t result = checksum & 0xFFFF;
|
||||
// DEBUG("CRC = %hx", result);
|
||||
DEBUG("CRC = %hx", result);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
crc_ = -1;
|
||||
fcs_ = -2;
|
||||
complete_ = false;
|
||||
frame_type_ = 0;
|
||||
frame_type_ = 0; // RF_DATA.
|
||||
}
|
||||
|
||||
void assign(data_type& data) {
|
||||
|
@ -133,13 +133,13 @@ public:
|
|||
fcs_ = (*it);
|
||||
++it;
|
||||
fcs_ |= (*it) << 8;
|
||||
// DEBUG("FCS = %hx", fcs_);
|
||||
DEBUG("FCS = %hx", fcs_);
|
||||
crc_ = compute_crc(data_.begin());
|
||||
complete_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Frame>
|
||||
template <typename Frame, size_t SIZE = 16>
|
||||
class FramePool
|
||||
{
|
||||
public:
|
||||
|
@ -147,7 +147,7 @@ public:
|
|||
typedef list<frame_type, constant_time_size<false>> FrameList;
|
||||
|
||||
private:
|
||||
static const uint16_t FRAME_COUNT = 16;
|
||||
static const uint16_t FRAME_COUNT = SIZE;
|
||||
frame_type frames_[FRAME_COUNT];
|
||||
FrameList free_list_;
|
||||
|
||||
|
@ -170,10 +170,12 @@ public:
|
|||
free_list_.pop_front();
|
||||
}
|
||||
taskEXIT_CRITICAL_FROM_ISR(x);
|
||||
DEBUG("Acquired frame %p (size after = %d)", result, free_list_.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
void release(frame_type* frame) {
|
||||
DEBUG("Released frame %p (size before = %d)", frame, free_list_.size());
|
||||
frame->clear();
|
||||
auto x = taskENTER_CRITICAL_FROM_ISR();
|
||||
free_list_.push_back(*frame);
|
||||
|
@ -186,7 +188,7 @@ typedef buffer::Pool<48> FrameSegmentPool; // 12K buffer of frames;
|
|||
extern FrameSegmentPool frameSegmentPool;
|
||||
|
||||
typedef Frame<FrameSegmentPool, &frameSegmentPool> IoFrame;
|
||||
typedef FramePool<IoFrame> IoFramePool;
|
||||
typedef FramePool<IoFrame, 48> IoFramePool;
|
||||
|
||||
IoFramePool& ioFramePool(void);
|
||||
|
||||
|
@ -195,13 +197,16 @@ IoFramePool& ioFramePool(void);
|
|||
* that causes functions using ioFramePool().release(frame) to use an
|
||||
* extremely large amount of stack space (4-6K vs. 24 bytes).
|
||||
*
|
||||
* This function merely acts as some sort of firewall to the stack usage.
|
||||
* This function merely acts as a compiler firewall to the stack usage.
|
||||
* It's own stack usage is minimal even though it is making the same call.
|
||||
*
|
||||
* @param frame
|
||||
*/
|
||||
void release(IoFrame* frame);
|
||||
|
||||
IoFrame* acquire(void);
|
||||
IoFrame* acquire_wait(void);
|
||||
|
||||
}}} // mobilinkd::tnc::hdlc
|
||||
|
||||
#endif // MOBILINKD__HDLC_FRAME_HPP_
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright 2017 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2017-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include <AudioLevel.hpp>
|
||||
#include <Log.h>
|
||||
#include "AudioLevel.hpp"
|
||||
#include "Log.h"
|
||||
#include "IOEventTask.h"
|
||||
#include "PortInterface.h"
|
||||
#include "PortInterface.hpp"
|
||||
|
@ -20,116 +20,137 @@
|
|||
|
||||
extern osMessageQId hdlcOutputQueueHandle;
|
||||
|
||||
void startIOEventTask(void const*)
|
||||
static PTT getPttStyle(const mobilinkd::tnc::kiss::Hardware& hardware)
|
||||
{
|
||||
initSerial();
|
||||
openSerial();
|
||||
|
||||
mobilinkd::tnc::audio::init_log_volume();
|
||||
mobilinkd::tnc::print_startup_banner();
|
||||
|
||||
|
||||
auto& hardware = mobilinkd::tnc::kiss::settings();
|
||||
if (!hardware.crc_ok()) {
|
||||
hardware.init();
|
||||
if (!hardware.load()) hardware.store();
|
||||
}
|
||||
hardware.debug();
|
||||
strcpy((char*)hardware.mycall, "WX9O");
|
||||
hardware.update_crc();
|
||||
|
||||
mobilinkd::tnc::audio::setAudioOutputLevel();
|
||||
mobilinkd::tnc::audio::setAudioInputLevels();
|
||||
|
||||
osMessagePut(audioInputQueueHandle,
|
||||
mobilinkd::tnc::audio::DEMODULATOR, osWaitForever);
|
||||
|
||||
/* Infinite loop */
|
||||
for(;;)
|
||||
{
|
||||
osEvent evt = osMessageGet(ioEventQueueHandle, osWaitForever);
|
||||
if (evt.status != osEventMessage) continue;
|
||||
|
||||
uint32_t cmd = evt.value.v;
|
||||
if (cmd < FLASH_BASE) // Assumes FLASH_BASE < SRAM_BASE.
|
||||
{
|
||||
switch (cmd) {
|
||||
case CMD_USER_BUTTON_DOWN:
|
||||
INFO("Button Down");
|
||||
osMessagePut(audioInputQueueHandle,
|
||||
mobilinkd::tnc::audio::AUTO_ADJUST_INPUT_LEVEL, osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle,
|
||||
mobilinkd::tnc::audio::DEMODULATOR, osWaitForever);
|
||||
break;
|
||||
case CMD_USER_BUTTON_UP:
|
||||
DEBUG("Button Up");
|
||||
break;
|
||||
case CMD_SET_PTT_SIMPLEX:
|
||||
getModulator().set_ptt(&simplexPtt);
|
||||
break;
|
||||
case CMD_SET_PTT_MULTIPLEX:
|
||||
getModulator().set_ptt(&multiplexPtt);
|
||||
break;
|
||||
default:
|
||||
WARN("unknown command = %04x", static_cast<unsigned int>(cmd));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
using mobilinkd::tnc::hdlc::IoFrame;
|
||||
|
||||
auto frame = static_cast<IoFrame*>(evt.value.p);
|
||||
|
||||
switch (frame->source()) {
|
||||
case IoFrame::RF_DATA:
|
||||
// DEBUG("RF frame");
|
||||
if (!mobilinkd::tnc::ioport->write(frame, 100)) {
|
||||
ERROR("Timed out sending frame");
|
||||
mobilinkd::tnc::hdlc::release(frame);
|
||||
}
|
||||
break;
|
||||
case IoFrame::SERIAL_DATA:
|
||||
// DEBUG("Serial frame");
|
||||
if ((frame->type() & 0x0F) == IoFrame::DATA) {
|
||||
if (osMessagePut(hdlcOutputQueueHandle, reinterpret_cast<uint32_t>(frame),
|
||||
osWaitForever) != osOK) {
|
||||
ERROR("Failed to write frame to TX queue");
|
||||
mobilinkd::tnc::hdlc::release(frame);
|
||||
}
|
||||
} else {
|
||||
mobilinkd::tnc::kiss::handle_frame(frame->type(), frame);
|
||||
}
|
||||
break;
|
||||
case IoFrame::DIGI_DATA:
|
||||
// DEBUG("Digi frame");
|
||||
if (osMessagePut(hdlcOutputQueueHandle, reinterpret_cast<uint32_t>(frame),
|
||||
osWaitForever) != osOK) {
|
||||
mobilinkd::tnc::hdlc::release(frame);
|
||||
}
|
||||
break;
|
||||
case IoFrame::FRAME_RETURN:
|
||||
mobilinkd::tnc::hdlc::release(frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hardware.options & KISS_OPTION_PTT_SIMPLEX ? PTT::SIMPLEX : PTT::MULTIPLEX;
|
||||
}
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
void startIOEventTask(void const*)
|
||||
{
|
||||
using namespace mobilinkd::tnc;
|
||||
|
||||
initSerial();
|
||||
openSerial();
|
||||
|
||||
print_startup_banner();
|
||||
|
||||
auto& hardware = kiss::settings();
|
||||
if (!hardware.load())
|
||||
{
|
||||
hardware.init();
|
||||
hardware.store();
|
||||
}
|
||||
|
||||
hardware.debug();
|
||||
|
||||
audio::init_log_volume();
|
||||
audio::setAudioOutputLevel();
|
||||
audio::setAudioInputLevels();
|
||||
setPtt(getPttStyle(hardware));
|
||||
|
||||
osMessagePut(audioInputQueueHandle, mobilinkd::tnc::audio::DEMODULATOR,
|
||||
osWaitForever);
|
||||
|
||||
/* Infinite loop */
|
||||
for (;;)
|
||||
{
|
||||
osEvent evt = osMessageGet(ioEventQueueHandle, osWaitForever);
|
||||
if (evt.status != osEventMessage)
|
||||
continue;
|
||||
|
||||
uint32_t cmd = evt.value.v;
|
||||
if (cmd < FLASH_BASE) // Assumes FLASH_BASE < SRAM_BASE.
|
||||
{
|
||||
switch (cmd) {
|
||||
case CMD_USER_BUTTON_DOWN:
|
||||
INFO("Button Down");
|
||||
osMessagePut(audioInputQueueHandle,
|
||||
mobilinkd::tnc::audio::AUTO_ADJUST_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle,
|
||||
mobilinkd::tnc::audio::DEMODULATOR, osWaitForever);
|
||||
break;
|
||||
case CMD_USER_BUTTON_UP:
|
||||
DEBUG("Button Up");
|
||||
break;
|
||||
case CMD_SET_PTT_SIMPLEX:
|
||||
getModulator().set_ptt(&simplexPtt);
|
||||
break;
|
||||
case CMD_SET_PTT_MULTIPLEX:
|
||||
getModulator().set_ptt(&multiplexPtt);
|
||||
break;
|
||||
default:
|
||||
WARN("unknown command = %04x", static_cast<unsigned int>(cmd));
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
using hdlc::IoFrame;
|
||||
|
||||
auto frame = static_cast<IoFrame*>(evt.value.p);
|
||||
|
||||
switch (frame->source()) {
|
||||
case IoFrame::RF_DATA:
|
||||
// DEBUG("RF frame");
|
||||
if (!ioport->write(frame, 100))
|
||||
{
|
||||
ERROR("Timed out sending frame");
|
||||
}
|
||||
break;
|
||||
case IoFrame::SERIAL_DATA:
|
||||
// DEBUG("Serial frame");
|
||||
if ((frame->type() & 0x0F) == IoFrame::DATA)
|
||||
{
|
||||
if (osMessagePut(hdlcOutputQueueHandle,
|
||||
reinterpret_cast<uint32_t>(frame),
|
||||
osWaitForever) != osOK)
|
||||
{
|
||||
ERROR("Failed to write frame to TX queue");
|
||||
hdlc::release(frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kiss::handle_frame(frame->type(), frame);
|
||||
}
|
||||
break;
|
||||
case IoFrame::DIGI_DATA:
|
||||
// DEBUG("Digi frame");
|
||||
if (osMessagePut(hdlcOutputQueueHandle,
|
||||
reinterpret_cast<uint32_t>(frame),
|
||||
osWaitForever) != osOK)
|
||||
{
|
||||
hdlc::release(frame);
|
||||
}
|
||||
break;
|
||||
case IoFrame::FRAME_RETURN:
|
||||
hdlc::release(frame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace mobilinkd {
|
||||
namespace tnc {
|
||||
|
||||
void print_startup_banner()
|
||||
{
|
||||
uint32_t* uid = (uint32_t*)0x1FFF7590; // STM32L4xx (same for 476 and 432)
|
||||
#ifdef KISS_LOGGING
|
||||
uint32_t* uid = (uint32_t*) UID_BASE; // STM32L4xx (same for 476 and 432)
|
||||
|
||||
INFO("%s version %s", mobilinkd::tnc::kiss::HARDWARE_VERSION, mobilinkd::tnc::kiss::FIRMWARE_VERSION);
|
||||
INFO("%s version %s", mobilinkd::tnc::kiss::HARDWARE_VERSION,
|
||||
mobilinkd::tnc::kiss::FIRMWARE_VERSION);
|
||||
INFO("CPU core clock: %luHz", SystemCoreClock);
|
||||
INFO(" Serial number: %08lX %08lX %08lX", uid[0], uid[1], uid[2]);
|
||||
INFO(" Device UID: %08lX %08lX %08lX", uid[0], uid[1], uid[2]);
|
||||
|
||||
uint8_t* version_ptr = (uint8_t*)0x1FFF6FF2;
|
||||
uint8_t* version_ptr = (uint8_t*) 0x1FFF6FF2;
|
||||
|
||||
int version = *version_ptr;
|
||||
|
||||
INFO("Bootloader version: 0x%02X", version);
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // mobilinkd::tnc
|
||||
}
|
||||
} // mobilinkd::tnc
|
||||
|
|
|
@ -4,69 +4,31 @@
|
|||
#ifndef MOBILINKD__TNC__IIR_FILTER_H_
|
||||
#define MOBILINKD__TNC__IIR_FILTER_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
|
||||
namespace mobilinkd { namespace tnc {
|
||||
|
||||
template <size_t N>
|
||||
struct TIirCoefficients {
|
||||
float b[N];
|
||||
float a[N];
|
||||
};
|
||||
|
||||
struct IirCoefficients {
|
||||
size_t size;
|
||||
const float* b;
|
||||
const float* a;
|
||||
|
||||
template <size_t N>
|
||||
IirCoefficients(const TIirCoefficients<N>& c)
|
||||
: size(N), b(c.b), a(c.b)
|
||||
{}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct IirFilter {
|
||||
|
||||
typedef float float_type;
|
||||
const float* numerator_;
|
||||
const float* denominator_;
|
||||
float history_[N];
|
||||
const std::array<float, N>& numerator_;
|
||||
const std::array<float, N>& denominator_;
|
||||
std::array<float, N> history_;
|
||||
size_t size_;
|
||||
|
||||
IirFilter(const float* b, const float* a)
|
||||
IirFilter(const std::array<float, N>& b,
|
||||
const std::array<float, N>& a)
|
||||
: numerator_(b), denominator_(a)
|
||||
, history_(), size_(N)
|
||||
{
|
||||
memset(history_, 0, N);
|
||||
}
|
||||
|
||||
IirFilter(const float* b, const float* a, size_t size)
|
||||
: numerator_(b), denominator_(a)
|
||||
, history_(), size_(size)
|
||||
{
|
||||
memset(history_, 0, N);
|
||||
}
|
||||
|
||||
template <size_t M>
|
||||
IirFilter(TIirCoefficients<M> c)
|
||||
: numerator_(c.b), denominator_(c.a)
|
||||
, history_(), size_(M)
|
||||
{
|
||||
memset(history_, 0, N);
|
||||
}
|
||||
|
||||
IirFilter(IirCoefficients c)
|
||||
: numerator_(c.b), denominator_(c.a)
|
||||
, history_(), size_(c.size)
|
||||
{
|
||||
memset(history_, 0, N);
|
||||
history_.fill(0.0f);
|
||||
}
|
||||
|
||||
~IirFilter() {}
|
||||
|
||||
float_type operator()(float_type input)
|
||||
float operator()(float input)
|
||||
{
|
||||
|
||||
for(size_t i = size_ - 1; i != 0; i--) history_[i] = history_[i - 1];
|
||||
|
@ -77,7 +39,7 @@ struct IirFilter {
|
|||
history_[0] -= denominator_[i] * history_[i];
|
||||
}
|
||||
|
||||
float_type result = 0;
|
||||
float result = 0;
|
||||
for (size_t i = 0; i != size_; i++) {
|
||||
result += numerator_[i] * history_[i];
|
||||
}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// Copyright 2015 Rob Riggs <rob@mobilinkd.com>
|
||||
// Copyright 2018-2019 Rob Riggs <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#include "KissHardware.hpp"
|
||||
#include "PortInterface.hpp"
|
||||
#include "AudioInput.hpp"
|
||||
#include "AudioLevel.hpp"
|
||||
#include "AFSKTestTone.hpp"
|
||||
#include "IOEventTask.h"
|
||||
#include <ModulatorTask.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <cstdio>
|
||||
|
||||
extern I2C_HandleTypeDef hi2c3;
|
||||
|
||||
namespace mobilinkd { namespace tnc { namespace kiss {
|
||||
|
||||
const char FIRMWARE_VERSION[] = "0.8.4";
|
||||
const char FIRMWARE_VERSION[] = "1.0.0";
|
||||
const char HARDWARE_VERSION[] = "Mobilinkd Nucleo32 Breadboard TNC";
|
||||
|
||||
Hardware& settings()
|
||||
|
@ -45,9 +45,7 @@ void Hardware::set_duplex(uint8_t value) {
|
|||
update_crc();
|
||||
}
|
||||
|
||||
#if 1
|
||||
|
||||
void reply8(uint8_t cmd, uint8_t result) {
|
||||
void reply8(uint8_t cmd, uint8_t result) {
|
||||
uint8_t data[2] { cmd, result };
|
||||
ioport->write(data, 2, 6, osWaitForever);
|
||||
}
|
||||
|
@ -58,26 +56,21 @@ void reply16(uint8_t cmd, uint16_t result) {
|
|||
}
|
||||
|
||||
inline void reply(uint8_t cmd, const uint8_t* data, uint16_t len) {
|
||||
auto buffer = (uint8_t*) malloc(len + 1);
|
||||
if (buffer == nullptr) return;
|
||||
uint8_t* buffer = static_cast<uint8_t*>(alloca(len + 1));
|
||||
buffer[0] = cmd;
|
||||
for (uint16_t i = 0; i != len and data[i] != 0; i++)
|
||||
for (uint16_t i = 0; i != len; i++)
|
||||
buffer[i + 1] = data[i];
|
||||
ioport->write(buffer, len + 1, 6, osWaitForever);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
inline void reply_ext(uint8_t ext, uint8_t cmd, const uint8_t* data, uint16_t len) {
|
||||
auto buffer = (uint8_t*) malloc(len + 2);
|
||||
if (buffer == nullptr) return;
|
||||
uint8_t* buffer = static_cast<uint8_t*>(alloca(len + 2));
|
||||
buffer[0] = ext;
|
||||
buffer[1] = cmd;
|
||||
for (uint16_t i = 0; i != len and data[i] != 0; i++)
|
||||
for (uint16_t i = 0; i != len; i++)
|
||||
buffer[i + 2] = data[i];
|
||||
ioport->write(buffer, len + 2, 6, osWaitForever);
|
||||
free(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Hardware::get_alias(uint8_t alias) {
|
||||
uint8_t result[14];
|
||||
|
@ -96,13 +89,21 @@ void Hardware::set_alias(const hdlc::IoFrame* frame) {
|
|||
UNUSED(frame);
|
||||
}
|
||||
|
||||
void Hardware::handle_request(hdlc::IoFrame* frame) {
|
||||
void Hardware::announce_input_settings()
|
||||
{
|
||||
reply16(hardware::GET_INPUT_GAIN, input_gain);
|
||||
reply8(hardware::GET_INPUT_TWIST, rx_twist);
|
||||
}
|
||||
|
||||
static AFSKTestTone testTone;
|
||||
AFSKTestTone& getAFSKTestTone() {
|
||||
static AFSKTestTone testTone;
|
||||
return testTone;
|
||||
}
|
||||
|
||||
void Hardware::handle_request(hdlc::IoFrame* frame)
|
||||
{
|
||||
auto it = frame->begin();
|
||||
uint8_t command = *it++;
|
||||
uint8_t v = *it;
|
||||
|
||||
switch (command) {
|
||||
case hardware::SEND_MARK:
|
||||
|
@ -113,31 +114,28 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
case hardware::SET_OUTPUT_TWIST:
|
||||
break;
|
||||
default:
|
||||
testTone.stop();
|
||||
getAFSKTestTone().stop();
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
|
||||
#if 0
|
||||
case hardware::SAVE:
|
||||
case hardware::SAVE_EEPROM_SETTINGS:
|
||||
save();
|
||||
reply8(hardware::OK, hardware::SAVE_EEPROM_SETTINGS);
|
||||
audio::adcState = audio::DEMODULATOR;
|
||||
update_crc();
|
||||
store();
|
||||
reply8(hardware::SAVE_EEPROM_SETTINGS, hardware::OK);
|
||||
break;
|
||||
#endif
|
||||
case hardware::POLL_INPUT_LEVEL:
|
||||
DEBUG("POLL_INPUT_VOLUME");
|
||||
reply8(hardware::POLL_INPUT_LEVEL, 0);
|
||||
osMessagePut(audioInputQueueHandle, audio::POLL_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
|
||||
osWaitForever);
|
||||
break;
|
||||
case hardware::STREAM_INPUT_LEVEL:
|
||||
DEBUG("STREAM_INPUT_VOLUME");
|
||||
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
DEBUG("STREAM_INPUT_VOLUME");
|
||||
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
break;
|
||||
case hardware::GET_BATTERY_LEVEL:
|
||||
DEBUG("GET_BATTERY_LEVEL");
|
||||
|
@ -150,23 +148,23 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
DEBUG("SEND_MARK");
|
||||
osMessagePut(audioInputQueueHandle, audio::IDLE,
|
||||
osWaitForever);
|
||||
testTone.mark();
|
||||
getAFSKTestTone().mark();
|
||||
break;
|
||||
case hardware::SEND_SPACE:
|
||||
DEBUG("SEND_SPACE");
|
||||
osMessagePut(audioInputQueueHandle, audio::IDLE,
|
||||
osWaitForever);
|
||||
testTone.space();
|
||||
getAFSKTestTone().space();
|
||||
break;
|
||||
case hardware::SEND_BOTH:
|
||||
DEBUG("SEND_BOTH");
|
||||
osMessagePut(audioInputQueueHandle, audio::IDLE,
|
||||
osWaitForever);
|
||||
testTone.both();
|
||||
getAFSKTestTone().both();
|
||||
break;
|
||||
case hardware::STOP_TX:
|
||||
DEBUG("STOP_TX");
|
||||
testTone.stop();
|
||||
getAFSKTestTone().stop();
|
||||
osMessagePut(audioInputQueueHandle, audio::IDLE,
|
||||
osWaitForever);
|
||||
break;
|
||||
|
@ -177,13 +175,16 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
break;
|
||||
case hardware::SET_OUTPUT_GAIN:
|
||||
DEBUG("SET_OUTPUT_VOLUME");
|
||||
output_gain = v;
|
||||
output_gain = *it << 8;
|
||||
++it;
|
||||
output_gain += *it;
|
||||
DEBUG("SET_OUTPUT_GAIN = %d", int(output_gain));
|
||||
audio::setAudioOutputLevel();
|
||||
update_crc();
|
||||
[[fallthrough]];
|
||||
case hardware::GET_OUTPUT_GAIN:
|
||||
DEBUG("GET_OUTPUT_VOLUME");
|
||||
reply8(hardware::GET_OUTPUT_GAIN, output_gain);
|
||||
reply16(hardware::GET_OUTPUT_GAIN, output_gain);
|
||||
break;
|
||||
|
||||
case hardware::STREAM_DCD_VALUE:
|
||||
|
@ -210,6 +211,14 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
osWaitForever);
|
||||
break;
|
||||
|
||||
case hardware::ADJUST_INPUT_LEVELS:
|
||||
DEBUG("ADJUST_INPUT_LEVELS");
|
||||
osMessagePut(audioInputQueueHandle, audio::AUTO_ADJUST_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
break;
|
||||
|
||||
case hardware::SET_VERBOSITY:
|
||||
DEBUG("SET_VERBOSITY");
|
||||
log_level = *it ? Log::Level::debug : Log::Level::warn;
|
||||
|
@ -220,23 +229,28 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
DEBUG("GET_VERBOSITY");
|
||||
reply8(hardware::GET_VERBOSITY, log_level == Log::Level::debug);
|
||||
break;
|
||||
#if 0
|
||||
case hardware::SET_LOWPASS_FREQ:
|
||||
lowpass_freq = (*it++ << 8);
|
||||
lowpass_freq = *it;
|
||||
// lowpass_freq = antiAliasFilter.setFilterFreq(lowpass_freq);
|
||||
audio::adcState = audio::UPDATE_SETTINGS;
|
||||
case hardware::GET_LOWPASS_FREQ:
|
||||
reply16(hardware::GET_LOWPASS_FREQ, lowpass_freq);
|
||||
case hardware::SET_INPUT_GAIN:
|
||||
input_gain = *it << 8;
|
||||
++it;
|
||||
input_gain += *it;
|
||||
DEBUG("SET_INPUT_GAIN = %d", input_gain);
|
||||
update_crc();
|
||||
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
[[fallthrough]];
|
||||
case hardware::GET_INPUT_GAIN:
|
||||
DEBUG("GET_INPUT_GAIN");
|
||||
reply16(hardware::GET_INPUT_GAIN, input_gain);
|
||||
break;
|
||||
#endif
|
||||
case hardware::SET_INPUT_TWIST:
|
||||
DEBUG("SET_INPUT_TWIST");
|
||||
rx_twist = *it;
|
||||
update_crc();
|
||||
osMessagePut(audioInputQueueHandle, audio::UPDATE_SETTINGS,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle, audio::DEMODULATOR,
|
||||
osMessagePut(audioInputQueueHandle, audio::STREAM_AMPLIFIED_INPUT_LEVEL,
|
||||
osWaitForever);
|
||||
[[fallthrough]];
|
||||
case hardware::GET_INPUT_TWIST:
|
||||
|
@ -295,14 +309,20 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
sizeof(HARDWARE_VERSION) - 1);
|
||||
break;
|
||||
|
||||
case hardware::GET_SERIAL_NUMBER:
|
||||
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");
|
||||
options &= ~KISS_OPTION_PTT_SIMPLEX;
|
||||
if (*it) {
|
||||
osMessagePut(ioEventQueueHandle, CMD_SET_PTT_MULTIPLEX, osWaitForever);
|
||||
options &= ~KISS_OPTION_PTT_SIMPLEX;
|
||||
osMessagePut(ioEventQueueHandle, CMD_SET_PTT_MULTIPLEX, osWaitForever);
|
||||
} else {
|
||||
options |= KISS_OPTION_PTT_SIMPLEX;
|
||||
osMessagePut(ioEventQueueHandle, CMD_SET_PTT_SIMPLEX, osWaitForever);
|
||||
options |= KISS_OPTION_PTT_SIMPLEX;
|
||||
osMessagePut(ioEventQueueHandle, CMD_SET_PTT_SIMPLEX, osWaitForever);
|
||||
}
|
||||
update_crc();
|
||||
break;
|
||||
|
@ -344,11 +364,15 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
|
||||
case hardware::GET_CAPABILITIES:
|
||||
DEBUG("GET_CAPABILITIES");
|
||||
reply16(hardware::GET_CAPABILITIES, 0);
|
||||
reply16(hardware::GET_CAPABILITIES,
|
||||
hardware::CAP_EEPROM_SAVE|
|
||||
hardware::CAP_ADJUST_INPUT|
|
||||
hardware::CAP_DFU_FIRMWARE);
|
||||
break;
|
||||
|
||||
case hardware::GET_ALL_VALUES:
|
||||
DEBUG("GET_ALL_VALUES");
|
||||
reply16(hardware::GET_API_VERSION, hardware::KISS_API_VERSION);
|
||||
osMessagePut(audioInputQueueHandle, audio::POLL_BATTERY_LEVEL,
|
||||
osWaitForever);
|
||||
osMessagePut(audioInputQueueHandle, audio::POLL_TWIST_LEVEL,
|
||||
|
@ -359,12 +383,12 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
sizeof(FIRMWARE_VERSION) - 1);
|
||||
reply(hardware::GET_HARDWARE_VERSION, (uint8_t*) HARDWARE_VERSION,
|
||||
sizeof(HARDWARE_VERSION) - 1);
|
||||
reply8(hardware::GET_USB_POWER_OFF, options & KISS_OPTION_VIN_POWER_OFF ? 0 : 1);
|
||||
reply8(hardware::GET_USB_POWER_ON, options & KISS_OPTION_VIN_POWER_ON ? 0 : 1);
|
||||
reply8(hardware::GET_OUTPUT_GAIN, output_gain);
|
||||
reply(hardware::GET_SERIAL_NUMBER, (uint8_t*) serial_number_64,
|
||||
sizeof(serial_number_64) - 1);
|
||||
reply16(hardware::GET_OUTPUT_GAIN, output_gain);
|
||||
reply8(hardware::GET_OUTPUT_TWIST, tx_twist);
|
||||
reply16(hardware::GET_INPUT_GAIN, input_gain);
|
||||
reply8(hardware::GET_INPUT_TWIST, rx_twist);
|
||||
reply8(hardware::GET_VERBOSITY, log_level == Log::Level::debug);
|
||||
reply8(hardware::GET_TXDELAY, txdelay);
|
||||
reply8(hardware::GET_PERSIST, ppersist);
|
||||
reply8(hardware::GET_TIMESLOT, slot);
|
||||
|
@ -372,7 +396,14 @@ void Hardware::handle_request(hdlc::IoFrame* frame) {
|
|||
reply8(hardware::GET_DUPLEX, duplex);
|
||||
reply8(hardware::GET_PTT_CHANNEL,
|
||||
options & KISS_OPTION_PTT_SIMPLEX ? 0 : 1);
|
||||
reply16(hardware::GET_CAPABILITIES, 0);
|
||||
reply16(hardware::GET_CAPABILITIES,
|
||||
hardware::CAP_EEPROM_SAVE|
|
||||
hardware::CAP_ADJUST_INPUT|
|
||||
hardware::CAP_DFU_FIRMWARE);
|
||||
reply16(hardware::GET_MIN_INPUT_GAIN, 0); // Constants for this FW
|
||||
reply16(hardware::GET_MAX_INPUT_GAIN, 4); // Constants for this FW
|
||||
reply8(hardware::GET_MIN_INPUT_TWIST, -3); // Constants for this FW
|
||||
reply8(hardware::GET_MAX_INPUT_TWIST, 9); // Constants for this FW
|
||||
|
||||
break;
|
||||
case hardware::EXTENDED_CMD:
|
||||
|
@ -411,21 +442,20 @@ void Hardware::handle_ext_request(hdlc::IoFrame* frame) {
|
|||
|
||||
bool Hardware::load()
|
||||
{
|
||||
|
||||
INFO("Loading settings from EEPROM");
|
||||
|
||||
auto tmp = std::make_unique<Hardware>();
|
||||
Hardware tmp;
|
||||
|
||||
if (!tmp) return false;
|
||||
memset(&tmp, 0, sizeof(Hardware));
|
||||
|
||||
memset(tmp.get(), 0, sizeof(Hardware));
|
||||
if (!I2C_Storage::load(tmp)) {
|
||||
ERROR("EEPROM read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!I2C_Storage::load(*tmp)) return false;
|
||||
|
||||
if (tmp->crc_ok())
|
||||
if (tmp.crc_ok())
|
||||
{
|
||||
memcpy(this, tmp.get(), sizeof(Hardware));
|
||||
|
||||
memcpy(this, &tmp, sizeof(Hardware));
|
||||
return true;
|
||||
}
|
||||
ERROR("EEPROM CRC error");
|
||||
|
@ -436,79 +466,69 @@ bool Hardware::store() const
|
|||
{
|
||||
INFO("Saving settings to EEPROM");
|
||||
|
||||
I2C_Storage::store(*this);
|
||||
if (!I2C_Storage::store(*this)) {
|
||||
ERROR("EEPROM write failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO("EEPROM saved checksum is: %04x (crc = %04x)", checksum, crc());
|
||||
|
||||
return true;
|
||||
return crc_ok();
|
||||
}
|
||||
|
||||
|
||||
bool I2C_Storage::load(void* ptr, size_t len)
|
||||
{
|
||||
uint32_t timeout = 1000;
|
||||
auto start = osKernelSysTick();
|
||||
if (HAL_I2C_Init(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
|
||||
DEBUG("Attempting to read %d bytes from EEPROM...", len);
|
||||
|
||||
uint32_t timeout = 1000; // systicks... milliseconds
|
||||
|
||||
auto tmp = static_cast<uint8_t*>(ptr);
|
||||
auto result = HAL_I2C_Mem_Read_DMA(&hi2c3, i2c_address, 0, I2C_MEMADD_SIZE_16BIT, tmp, len);
|
||||
if (result != HAL_OK) Error_Handler();
|
||||
auto result = HAL_I2C_Mem_Read(&hi2c3, i2c_address, 0,
|
||||
I2C_MEMADD_SIZE_16BIT, tmp, len, timeout);
|
||||
if (result != HAL_OK) CxxErrorHandler();
|
||||
|
||||
while (HAL_I2C_GetState(&hi2c3) != HAL_I2C_STATE_READY)
|
||||
{
|
||||
if (osKernelSysTick() > start + timeout) return false;
|
||||
osThreadYield();
|
||||
}
|
||||
if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2C_Storage::store(const void* ptr, size_t len)
|
||||
{
|
||||
if (HAL_I2C_Init(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
|
||||
auto tmp = const_cast<uint8_t*>(static_cast<const uint8_t*>(ptr));
|
||||
|
||||
uint32_t timeout = 1000;
|
||||
|
||||
auto start = osKernelSysTick();
|
||||
|
||||
uint32_t index = 0;
|
||||
size_t remaining = len;
|
||||
while (remaining > page_size)
|
||||
{
|
||||
auto result = HAL_I2C_Mem_Write_DMA(&hi2c3, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, page_size);
|
||||
if (result == HAL_BUSY)
|
||||
{
|
||||
if (osKernelSysTick() > start + timeout) return false;
|
||||
osThreadYield();
|
||||
continue;
|
||||
auto result = HAL_I2C_Mem_Write(&hi2c3, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, page_size, 20);
|
||||
if (result != HAL_OK) {
|
||||
ERROR("EEPROM write block error = %lu.", hi2c3.ErrorCode);
|
||||
if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
return false;
|
||||
}
|
||||
if (result != HAL_OK) Error_Handler();
|
||||
osDelay(write_time);
|
||||
index += page_size;
|
||||
remaining -= page_size;
|
||||
while (HAL_I2C_GetState(&hi2c3) != HAL_I2C_STATE_READY)
|
||||
{
|
||||
if (osKernelSysTick() > start + timeout) return false;
|
||||
osThreadYield();
|
||||
}
|
||||
}
|
||||
|
||||
while (remaining) {
|
||||
auto result = HAL_I2C_Mem_Write_DMA(&hi2c3, i2c_address << 8, index, I2C_MEMADD_SIZE_16BIT, tmp + index, remaining);
|
||||
if (result == HAL_BUSY) {
|
||||
osThreadYield();
|
||||
continue;
|
||||
auto result = HAL_I2C_Mem_Write(&hi2c3, i2c_address, index, I2C_MEMADD_SIZE_16BIT, tmp + index, remaining, 20);
|
||||
if (result != HAL_OK) {
|
||||
ERROR("EEPROM write remainder error = %lu.", hi2c3.ErrorCode);
|
||||
if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
return false;
|
||||
}
|
||||
if (result != HAL_OK) Error_Handler();
|
||||
osDelay(write_time);
|
||||
index += remaining;
|
||||
remaining = 0;
|
||||
while (HAL_I2C_GetState(&hi2c3) != HAL_I2C_STATE_READY)
|
||||
{
|
||||
if (osKernelSysTick() > start + timeout) return false;
|
||||
osThreadYield();
|
||||
}
|
||||
}
|
||||
|
||||
if (HAL_I2C_DeInit(&hi2c3) != HAL_OK) CxxErrorHandler();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2015 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// Copyright 2015-2019 Mobilinkd LLC <rob@mobilinkd.com>
|
||||
// All rights reserved.
|
||||
|
||||
#ifndef MOBILINKD__TNC__KISS_HARDWARE_HPP_
|
||||
#define MOBILINKD__TNC__KISS_HARDWARE_HPP_
|
||||
#pragma once
|
||||
|
||||
#include <Log.h>
|
||||
#include "Log.h"
|
||||
#include "HdlcFrame.hpp"
|
||||
#include "AFSKTestTone.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
@ -18,93 +18,121 @@ namespace mobilinkd { namespace tnc { namespace kiss {
|
|||
extern const char FIRMWARE_VERSION[];
|
||||
extern const char HARDWARE_VERSION[];
|
||||
|
||||
AFSKTestTone& getAFSKTestTone();
|
||||
|
||||
namespace hardware {
|
||||
|
||||
const uint16_t CAP_DCD = 0x0001;
|
||||
const uint16_t CAP_SQUELCH = 0x0002;
|
||||
const uint16_t CAP_INPUT_ATTEN = 0x0004;
|
||||
const uint16_t CAP_FIRMWARE_VERSION = 0x0008;
|
||||
const uint16_t CAP_BATTERY_LEVEL = 0x0010;
|
||||
const uint16_t CAP_BT_CONN_TRACK = 0x0020;
|
||||
const uint16_t CAP_BT_NAME_CHANGE = 0x0040;
|
||||
const uint16_t CAP_BT_PIN_CHANGE = 0x0080;
|
||||
const uint16_t CAP_VERBOSE_ERROR = 0x0100;
|
||||
const uint16_t CAP_EEPROM_SAVE = 0x0200;
|
||||
const uint16_t CAP_ADJUST_INPUT = 0x0400;
|
||||
/**
|
||||
* This indicates the API version of the device. API versions are used to
|
||||
* indicate to the user that the config app may need to be upgraded because
|
||||
* the device is using a newer configuration API.
|
||||
*
|
||||
* The minor version should be updated whenever the API is extended (new
|
||||
* GET/SET or CAP types added.
|
||||
*
|
||||
* The major version should be updated whenever non-backwards compatible
|
||||
* changes to the API are made.
|
||||
*/
|
||||
constexpr const uint16_t KISS_API_VERSION = 0x0200;
|
||||
|
||||
const uint8_t SAVE = 0; // Save settings to EEPROM.
|
||||
const uint8_t SET_OUTPUT_GAIN = 1;
|
||||
const uint8_t SET_INPUT_GAIN = 2;
|
||||
const uint8_t SET_SQUELCH_LEVEL = 3;
|
||||
const uint8_t POLL_INPUT_LEVEL = 4;
|
||||
const uint8_t STREAM_INPUT_LEVEL = 5;
|
||||
const uint8_t GET_BATTERY_LEVEL = 6;
|
||||
const uint8_t SEND_MARK = 7;
|
||||
const uint8_t SEND_SPACE = 8;
|
||||
const uint8_t SEND_BOTH = 9;
|
||||
const uint8_t STOP_TX = 10;
|
||||
const uint8_t RESET = 11;
|
||||
const uint8_t GET_OUTPUT_GAIN = 12;
|
||||
const uint8_t GET_INPUT_ATTEN = 13;
|
||||
const uint8_t GET_SQUELCH_LEVEL = 14;
|
||||
const uint8_t STREAM_DCD_VALUE = 15;
|
||||
constexpr const uint16_t CAP_DCD = 0x0100;
|
||||
constexpr const uint16_t CAP_SQUELCH = 0x0200;
|
||||
constexpr const uint16_t CAP_INPUT_ATTEN = 0x0400;
|
||||
constexpr const uint16_t CAP_FIRMWARE_VERSION = 0x0800;
|
||||
constexpr const uint16_t CAP_BATTERY_LEVEL = 0x1000;
|
||||
constexpr const uint16_t CAP_BT_CONN_TRACK = 0x2000;
|
||||
constexpr const uint16_t CAP_BT_NAME_CHANGE = 0x4000;
|
||||
constexpr const uint16_t CAP_BT_PIN_CHANGE = 0x8000;
|
||||
constexpr const uint16_t CAP_VERBOSE_ERROR = 0x0001;
|
||||
constexpr const uint16_t CAP_EEPROM_SAVE = 0x0002;
|
||||
constexpr const uint16_t CAP_ADJUST_INPUT = 0x0004; // Auto-adjust input levels.
|
||||
constexpr const uint16_t CAP_DFU_FIRMWARE = 0x0008; // DFU firmware style.
|
||||
|
||||
const uint8_t SET_VERBOSITY = 16;
|
||||
const uint8_t GET_VERBOSITY = 17;
|
||||
constexpr const uint8_t SAVE = 0; // Save settings to EEPROM.
|
||||
constexpr const uint8_t SET_OUTPUT_GAIN = 1;
|
||||
constexpr const uint8_t SET_INPUT_GAIN = 2;
|
||||
constexpr const uint8_t SET_SQUELCH_LEVEL = 3; // deprecated.
|
||||
constexpr const uint8_t POLL_INPUT_LEVEL = 4;
|
||||
constexpr const uint8_t STREAM_INPUT_LEVEL = 5;
|
||||
constexpr const uint8_t GET_BATTERY_LEVEL = 6;
|
||||
constexpr const uint8_t SEND_MARK = 7;
|
||||
constexpr const uint8_t SEND_SPACE = 8;
|
||||
constexpr const uint8_t SEND_BOTH = 9;
|
||||
constexpr const uint8_t STOP_TX = 10;
|
||||
constexpr const uint8_t RESET = 11;
|
||||
constexpr const uint8_t GET_OUTPUT_GAIN = 12;
|
||||
constexpr const uint8_t GET_INPUT_GAIN = 13;
|
||||
constexpr const uint8_t GET_SQUELCH_LEVEL = 14;
|
||||
constexpr const uint8_t STREAM_DCD_VALUE = 15;
|
||||
|
||||
const uint8_t SET_INPUT_OFFSET = 18;
|
||||
const uint8_t GET_INPUT_OFFSET = 19;
|
||||
const uint8_t SET_OUTPUT_OFFSET = 20;
|
||||
const uint8_t GET_OUTPUT_OFFSET = 21;
|
||||
const uint8_t SET_LOWPASS_FREQ = 22;
|
||||
const uint8_t GET_LOWPASS_FREQ = 23;
|
||||
const uint8_t SET_INPUT_TWIST = 24;
|
||||
const uint8_t GET_INPUT_TWIST = 25;
|
||||
const uint8_t SET_OUTPUT_TWIST = 26;
|
||||
const uint8_t GET_OUTPUT_TWIST = 27;
|
||||
constexpr const uint8_t SET_VERBOSITY = 16;
|
||||
constexpr const uint8_t GET_VERBOSITY = 17;
|
||||
|
||||
const uint8_t STREAM_RAW_INPUT = 28;
|
||||
const uint8_t STREAM_AMPLIFIED_INPUT = 29;
|
||||
const uint8_t STREAM_FILTERED_INPUT = 30;
|
||||
const uint8_t STREAM_OUTPUT = 31;
|
||||
constexpr const uint8_t SET_INPUT_OFFSET = 18;
|
||||
constexpr const uint8_t GET_INPUT_OFFSET = 19;
|
||||
constexpr const uint8_t SET_OUTPUT_OFFSET = 20;
|
||||
constexpr const uint8_t GET_OUTPUT_OFFSET = 21;
|
||||
constexpr const uint8_t SET_LOWPASS_FREQ = 22;
|
||||
constexpr const uint8_t GET_LOWPASS_FREQ = 23;
|
||||
constexpr const uint8_t SET_INPUT_TWIST = 24;
|
||||
constexpr const uint8_t GET_INPUT_TWIST = 25;
|
||||
constexpr const uint8_t SET_OUTPUT_TWIST = 26;
|
||||
constexpr const uint8_t GET_OUTPUT_TWIST = 27;
|
||||
|
||||
const uint8_t OK = 32; // Acknowledge SET commands.
|
||||
constexpr const uint8_t STREAM_RAW_INPUT = 28;
|
||||
constexpr const uint8_t STREAM_AMPLIFIED_INPUT = 29;
|
||||
constexpr const uint8_t STREAM_FILTERED_INPUT = 30;
|
||||
constexpr const uint8_t STREAM_OUTPUT = 31;
|
||||
|
||||
const uint8_t GET_TXDELAY = 33;
|
||||
const uint8_t GET_PERSIST = 34;
|
||||
const uint8_t GET_TIMESLOT = 35;
|
||||
const uint8_t GET_TXTAIL = 36;
|
||||
const uint8_t GET_DUPLEX = 37;
|
||||
constexpr const uint8_t OK = 32; // Acknowledge SET commands.
|
||||
|
||||
const uint8_t GET_FIRMWARE_VERSION = 40;
|
||||
const uint8_t GET_HARDWARE_VERSION = 41;
|
||||
const uint8_t SAVE_EEPROM_SETTINGS = 42;
|
||||
const uint8_t ADJUST_INPUT_LEVELS = 43;
|
||||
const uint8_t POLL_INPUT_TWIST = 44;
|
||||
const uint8_t STREAM_AVG_INPUT_TWIST = 45;
|
||||
const uint8_t STREAM_INPUT_TWIST = 46;
|
||||
constexpr const uint8_t GET_TXDELAY = 33;
|
||||
constexpr const uint8_t GET_PERSIST = 34;
|
||||
constexpr const uint8_t GET_TIMESLOT = 35;
|
||||
constexpr const uint8_t GET_TXTAIL = 36;
|
||||
constexpr const uint8_t GET_DUPLEX = 37;
|
||||
|
||||
const uint8_t SET_BLUETOOTH_NAME = 65;
|
||||
const uint8_t GET_BLUETOOTH_NAME = 66;
|
||||
const uint8_t SET_BLUETOOTH_PIN = 67; // Danger Will Robinson.
|
||||
const uint8_t GET_BLUETOOTH_PIN = 68;
|
||||
const uint8_t SET_BT_CONN_TRACK = 69; // Bluetooth connection tracking
|
||||
const uint8_t GET_BT_CONN_TRACK = 70; // Bluetooth connection tracking
|
||||
const uint8_t SET_BT_MAJOR_CLASS = 71; // Bluetooth Major Class
|
||||
const uint8_t GET_BT_MAJOR_CLASS = 72; // Bluetooth Major Class
|
||||
constexpr const uint8_t GET_FIRMWARE_VERSION = 40;
|
||||
constexpr const uint8_t GET_HARDWARE_VERSION = 41;
|
||||
constexpr const uint8_t SAVE_EEPROM_SETTINGS = 42;
|
||||
constexpr const uint8_t ADJUST_INPUT_LEVELS = 43; // Auto-adjust levels.
|
||||
constexpr const uint8_t POLL_INPUT_TWIST = 44;
|
||||
constexpr const uint8_t STREAM_AVG_INPUT_TWIST = 45;
|
||||
constexpr const uint8_t STREAM_INPUT_TWIST = 46;
|
||||
constexpr const uint8_t GET_SERIAL_NUMBER = 47;
|
||||
constexpr const uint8_t GET_MAC_ADDRESS = 48;
|
||||
constexpr const uint8_t GET_DATETIME = 49;
|
||||
constexpr const uint8_t SET_DATETIME = 50;
|
||||
constexpr const uint8_t GET_ERROR_MSG = 51;
|
||||
|
||||
const uint8_t SET_USB_POWER_ON = 73; // Power on when USB power available
|
||||
const uint8_t GET_USB_POWER_ON = 74;
|
||||
const uint8_t SET_USB_POWER_OFF = 75; // Power off when USB power unavailable
|
||||
const uint8_t GET_USB_POWER_OFF = 76;
|
||||
const uint8_t SET_BT_POWER_OFF = 77; // Power off after n seconds w/o BT conn
|
||||
const uint8_t GET_BT_POWER_OFF = 78;
|
||||
constexpr const uint8_t SET_BLUETOOTH_NAME = 65;
|
||||
constexpr const uint8_t GET_BLUETOOTH_NAME = 66;
|
||||
constexpr const uint8_t SET_BLUETOOTH_PIN = 67; // Danger Will Robinson.
|
||||
constexpr const uint8_t GET_BLUETOOTH_PIN = 68;
|
||||
constexpr const uint8_t SET_BT_CONN_TRACK = 69; // Bluetooth connection tracking
|
||||
constexpr const uint8_t GET_BT_CONN_TRACK = 70; // Bluetooth connection tracking
|
||||
constexpr const uint8_t SET_BT_MAJOR_CLASS = 71; // Bluetooth Major Class
|
||||
constexpr const uint8_t GET_BT_MAJOR_CLASS = 72; // Bluetooth Major Class
|
||||
|
||||
const uint8_t SET_PTT_CHANNEL = 79; // Which PTT line to use (currently 0 or 1,
|
||||
const uint8_t GET_PTT_CHANNEL = 80; // multiplex or simplex)
|
||||
constexpr const uint8_t SET_USB_POWER_ON = 73; // Power on when USB power available
|
||||
constexpr const uint8_t GET_USB_POWER_ON = 74;
|
||||
constexpr const uint8_t SET_USB_POWER_OFF = 75; // Power off when USB power unavailable
|
||||
constexpr const uint8_t GET_USB_POWER_OFF = 76;
|
||||
constexpr const uint8_t SET_BT_POWER_OFF = 77; // Power off after n seconds w/o BT conn
|
||||
constexpr const uint8_t GET_BT_POWER_OFF = 78;
|
||||
|
||||
const uint8_t GET_CAPABILITIES = 126; ///< Send all capabilities.
|
||||
const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions.
|
||||
constexpr const uint8_t SET_PTT_CHANNEL = 79; // Which PTT line to use (currently 0 or 1,
|
||||
constexpr const uint8_t GET_PTT_CHANNEL = 80; // multiplex or simplex)
|
||||
|
||||
constexpr const uint8_t GET_MIN_OUTPUT_TWIST = 119; ///< int8_t (may be negative).
|
||||
constexpr const uint8_t GET_MAX_OUTPUT_TWIST = 120; ///< int8_t (may be negative).
|
||||
constexpr const uint8_t GET_MIN_INPUT_TWIST = 121; ///< int8_t (may be negative).
|
||||
constexpr const uint8_t GET_MAX_INPUT_TWIST = 122; ///< int8_t (may be negative).
|
||||
constexpr const uint8_t GET_API_VERSION = 123; ///< uint16_t (major/minor)
|
||||
constexpr const uint8_t GET_MIN_INPUT_GAIN = 124; ///< int8_t (may be negative/attenuated).
|
||||
constexpr const uint8_t GET_MAX_INPUT_GAIN = 125; ///< int8_t (may be negative/attenuated).
|
||||
constexpr const uint8_t GET_CAPABILITIES = 126; ///< Send all capabilities.
|
||||
constexpr const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions.
|
||||
|
||||
/**
|
||||
* Extended commands are two+ bytes in length. They start at 80:00
|
||||
|
@ -114,20 +142,20 @@ const uint8_t GET_ALL_VALUES = 127; ///< Send all settings & versions.
|
|||
* If needed, the commands can be extended to 9 nibbles (D0 - DF),
|
||||
* 13 nibbles (E0-EF) and 17 nibbles (F0-FF).
|
||||
*/
|
||||
const uint8_t EXTENDED_CMD = 128;
|
||||
constexpr const uint8_t EXTENDED_CMD = 128;
|
||||
|
||||
const uint8_t EXT_OK = 0;
|
||||
const uint8_t EXT_GET_MODEM_TYPE = 1;
|
||||
const uint8_t EXT_SET_MODEM_TYPE = 2;
|
||||
const uint8_t EXT_GET_MODEM_TYPES = 3; ///< Return a list of supported modem types
|
||||
constexpr const uint8_t EXT_OK = 0;
|
||||
constexpr const uint8_t EXT_GET_MODEM_TYPE = 1;
|
||||
constexpr const uint8_t EXT_SET_MODEM_TYPE = 2;
|
||||
constexpr const uint8_t EXT_GET_MODEM_TYPES = 3; ///< Return a list of supported modem types
|
||||
|
||||
const uint8_t EXT_GET_ALIASES = 8; ///< Number of aliases supported
|
||||
const uint8_t EXT_GET_ALIAS = 9; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
|
||||
const uint8_t EXT_SET_ALIAS = 10; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
|
||||
constexpr const uint8_t EXT_GET_ALIASES = 8; ///< Number of aliases supported
|
||||
constexpr const uint8_t EXT_GET_ALIAS = 9; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
|
||||
constexpr const uint8_t EXT_SET_ALIAS = 10; ///< Alias number (uint8_t), 8 characters, 5 bytes (set, use, insert_id, preempt, hops)
|
||||
|
||||
const uint8_t EXT_GET_BEACONS = 12; ///< Number of beacons supported
|
||||
const uint8_t EXT_GET_BEACON = 13; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
|
||||
const uint8_t EXT_SET_BEACON = 14; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
|
||||
constexpr const uint8_t EXT_GET_BEACONS = 12; ///< Number of beacons supported
|
||||
constexpr const uint8_t EXT_GET_BEACON = 13; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
|
||||
constexpr const uint8_t EXT_SET_BEACON = 14; ///< Beacon number (uint8_t), uint16_t interval in seconds, 3 NUL terminated strings (callsign, path, text)
|
||||
|
||||
constexpr const uint8_t EXT_GET_MYCALL = 16; ///< MYCALL callsign = 8 characters. right padded with NUL.
|
||||
constexpr const uint8_t EXT_SET_MYCALL = 17; ///< MYCALL callsign = 8 characters. right padded with NUL.
|
||||
|
@ -143,7 +171,7 @@ constexpr const uint8_t MODEM_TYPE_MFSK16 = 6;
|
|||
#define KISS_OPTION_CONN_TRACK 0x01
|
||||
#define KISS_OPTION_VERBOSE 0x02
|
||||
#define KISS_OPTION_VIN_POWER_ON 0x04 // Power on when plugged in to USB
|
||||
#define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when plugged in to USB
|
||||
#define KISS_OPTION_VIN_POWER_OFF 0x08 // Power off when unplugged from USB
|
||||
#define KISS_OPTION_PTT_SIMPLEX 0x10 // Simplex PTT (the default)
|
||||
|
||||
const char TOCALL[] = "APML00"; // Update for every feature change.
|
||||
|
@ -223,7 +251,11 @@ struct Hardware
|
|||
}
|
||||
|
||||
bool crc_ok() const {
|
||||
return crc() == checksum;
|
||||
auto result = (crc() == checksum);
|
||||
if (!result) {
|
||||
WARN("CRC mismatch %04x != %04x", checksum, crc());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,14 +265,6 @@ struct Hardware
|
|||
*/
|
||||
void init()
|
||||
{
|
||||
if (crc_ok()) {
|
||||
DEBUG("CRC OK");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG("CRC FAILED");
|
||||
DEBUG("checksum 0x%04x != CRC 0x%04x", checksum, crc());
|
||||
|
||||
txdelay = 30;
|
||||
ppersist = 64;
|
||||
slot = 10;
|
||||
|
@ -248,7 +272,7 @@ struct Hardware
|
|||
duplex = 0;
|
||||
modem_type = ModemType::AFSK1200;
|
||||
output_gain = 63;
|
||||
input_gain = 0;
|
||||
input_gain = 0; // 0-4 on TNC3
|
||||
tx_twist = 50;
|
||||
rx_twist = 0;
|
||||
log_level = Log::Level::debug;
|
||||
|
@ -256,7 +280,7 @@ struct Hardware
|
|||
options = KISS_OPTION_PTT_SIMPLEX;
|
||||
|
||||
/// Callsign. Pad unused with NUL.
|
||||
strcpy((char*)mycall, "MYCALL");
|
||||
strcpy((char*)mycall, "NOCALL");
|
||||
|
||||
dedupe_seconds = 30;
|
||||
memset(aliases, 0, sizeof(aliases));
|
||||
|
@ -265,12 +289,11 @@ struct Hardware
|
|||
|
||||
updatePtt();
|
||||
|
||||
debug();
|
||||
|
||||
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);
|
||||
|
@ -304,13 +327,11 @@ struct Hardware
|
|||
DEBUG(" frequency (secs): %d", (int)b.seconds);
|
||||
}
|
||||
DEBUG("Checksum: %04xs", checksum);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 1
|
||||
bool load();
|
||||
|
||||
bool store() const;
|
||||
#endif
|
||||
|
||||
void set_txdelay(uint8_t value);
|
||||
void set_ppersist(uint8_t value);
|
||||
|
@ -325,16 +346,18 @@ struct Hardware
|
|||
void get_alias(uint8_t alias);
|
||||
void set_alias(const hdlc::IoFrame* frame);
|
||||
|
||||
void announce_input_settings();
|
||||
|
||||
}; // 812 bytes
|
||||
|
||||
extern Hardware& settings();
|
||||
|
||||
struct I2C_Storage
|
||||
{
|
||||
constexpr static const uint16_t i2c_address{0xA0};
|
||||
constexpr static const uint16_t capacity{4096};
|
||||
constexpr static const uint16_t page_size{32};
|
||||
constexpr static const uint32_t write_time{5};
|
||||
constexpr static const uint16_t i2c_address{EEPROM_ADDRESS};
|
||||
constexpr static const uint16_t capacity{EEPROM_CAPACITY};
|
||||
constexpr static const uint16_t page_size{EEPROM_PAGE_SIZE};
|
||||
constexpr static const uint32_t write_time{EEPROM_WRITE_TIME};
|
||||
|
||||
static bool load(void* ptr, size_t len);
|
||||
|
||||
|
@ -356,5 +379,3 @@ void reply8(uint8_t cmd, uint8_t result) __attribute__((noinline));
|
|||
void reply16(uint8_t cmd, uint16_t result) __attribute__((noinline));
|
||||
|
||||
}}} // mobilinkd::tnc::kiss
|
||||
|
||||
#endif // INMOBILINKD__TNC__KISS_HARDWARE_HPP_C_KISSHARDWARE_HPP_
|
||||
|
|
Ładowanie…
Reference in New Issue