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.

m17
Rob Riggs 2019-06-22 21:53:34 -05:00
rodzic 1d228b0413
commit 777919da49
32 zmienionych plików z 1706 dodań i 1534 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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
*/

Wyświetl plik

@ -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
*/

Wyświetl plik

@ -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
*/

Wyświetl plik

@ -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
*/

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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_

Wyświetl plik

@ -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_

Wyświetl plik

@ -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);
}
}
}

Wyświetl plik

@ -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_;}
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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_

Wyświetl plik

@ -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);

Wyświetl plik

@ -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

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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_

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -8,6 +8,7 @@
#include <arm_math.h>
#include <complex>
#include <cmath>
namespace mobilinkd { namespace tnc {

Wyświetl plik

@ -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) {

Wyświetl plik

@ -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

Wyświetl plik

@ -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_

Wyświetl plik

@ -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

Wyświetl plik

@ -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_

Wyświetl plik

@ -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

Wyświetl plik

@ -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];
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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_