Sync NucleoTNC firmware with TNC3 v2.1.8 codebase. Upgrade compiler to GCC 10.2. Upgrade to C++20 for std::span. Changes to reduce stack utilization. Reduce jitter by adjusting clocks & interrupt priority. Fix serial port timeout on large packets. Use floating point for M17 modulator. Fix LICH decode defect.

master v2.1.8
Rob Riggs 2021-02-21 20:48:09 -06:00
rodzic bc01e2840f
commit eb478b443a
23 zmienionych plików z 1020 dodań i 145 usunięć

Wyświetl plik

@ -120,11 +120,11 @@
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.level.1924121031" name="Debug level" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.level" useByScannerDiscovery="true"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.format.437976635" name="Debug format" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.format" useByScannerDiscovery="true"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.name.1999567545" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.name" useByScannerDiscovery="false" value="GNU Tools for ARM Embedded Processors" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.id.827716078" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.id" value="962691777" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.id.827716078" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.id" value="1287942917" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.command.prefix.1226916242" name="Prefix" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.command.prefix" useByScannerDiscovery="false" value="arm-none-eabi-" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.command.c.1790126668" name="C compiler" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.command.c" useByScannerDiscovery="false" value="gcc" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.command.cpp.435521309" name="C++ compiler" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.command.cpp" useByScannerDiscovery="false" value="g++" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family.1588366312" name="ARM family (-mcpu)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.mcpu.cortex-m4" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family.1588366312" name="Arm family (-mcpu)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.mcpu.cortex-m4" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.abi.1503489612" name="Float ABI" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.abi" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.abi.softfp" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.unit.310369970" name="FPU Type" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.unit" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.fpu.unit.fpv4spd16" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.optimization.noinlinefunctions.937089372" name="Do not inline functions (-fno-inline-functions)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.optimization.noinlinefunctions" useByScannerDiscovery="true" value="true" valueType="boolean"/>
@ -207,14 +207,14 @@
<listOptionValue builtIn="false" value="__weak=__attribute__((weak))"/>
<listOptionValue builtIn="false" value="NUCLEOTNC=1"/>
</option>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std.1698432379" name="Language standard" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std.gnucpp1z" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion.1773636385" name="ABI version" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion.9" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std.1698432379" name="Language standard" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.std.gnucpp2a" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion.1773636385" name="ABI version" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion" useByScannerDiscovery="true" value="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.abiversion.13" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.noexceptions.1798306214" name="Do not use exceptions (-fno-exceptions)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.noexceptions" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nortti.433035511" name="Do not use RTTI (-fno-rtti)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nortti" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nousecxaatexit.1502881352" name="Do not use _cxa_atexit() (-fno-use-cxa-atexit)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nousecxaatexit" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nothreadsafestatics.2021365039" name="Do not use thread-safe statics (-fno-threadsafe-statics)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.nothreadsafestatics" useByScannerDiscovery="true" value="true" valueType="boolean"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.warnabi.465405510" name="Warn on ABI violations (-Wabi)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.warnabi" useByScannerDiscovery="true" value="false" valueType="boolean"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.otherwarnings.1044945797" name="Other warning flags" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.otherwarnings" useByScannerDiscovery="true" value="-Wno-register" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.otherwarnings.1044945797" name="Other warning flags" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.cpp.compiler.otherwarnings" useByScannerDiscovery="true" value="-Wno-register -Wall -Wextra" valueType="string"/>
<inputType id="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.compiler.input.906017231" superClass="ilg.gnuarmeclipse.managedbuild.cross.tool.cpp.compiler.input"/>
</tool>
<tool id="ilg.gnuarmeclipse.managedbuild.cross.tool.c.linker.110190447" name="GNU ARM Cross C Linker" superClass="ilg.gnuarmeclipse.managedbuild.cross.tool.c.linker">
@ -251,6 +251,9 @@
</tool>
</toolChain>
</folderInfo>
<sourceEntries>
<entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name=""/>
</sourceEntries>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
@ -282,7 +285,7 @@
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.format.51697857" name="Debug format" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.debugging.format" useByScannerDiscovery="true"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.name.1445567365" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.toolchain.name" useByScannerDiscovery="false" value="GNU Tools for ARM Embedded Processors" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.architecture.1680967491" name="Architecture" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.architecture" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.architecture.arm" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family.1498989241" name="ARM family (-mcpu)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.mcpu.cortex-m4" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family.1498989241" name="Arm family (-mcpu)" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.family" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.mcpu.cortex-m4" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.instructionset.1207350265" name="Instruction set" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.instructionset" useByScannerDiscovery="false" value="ilg.gnuarmeclipse.managedbuild.cross.option.arm.target.instructionset.thumb" valueType="enumerated"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.command.prefix.318899792" name="Prefix" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.command.prefix" useByScannerDiscovery="false" value="arm-none-eabi-" valueType="string"/>
<option id="ilg.gnuarmeclipse.managedbuild.cross.option.command.c.1240926089" name="C compiler" superClass="ilg.gnuarmeclipse.managedbuild.cross.option.command.c" useByScannerDiscovery="false" value="gcc" valueType="string"/>
@ -560,4 +563,4 @@
</profile>
</scannerConfigBuildInfo>
</storageModule>
</cproject>
</cproject>

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -59,14 +59,14 @@
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 7 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)4096)
#define configTOTAL_HEAP_SIZE ((size_t)3072)
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configCHECK_FOR_STACK_OVERFLOW 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
#define configUSE_TICKLESS_IDLE 1
#define configUSE_TICKLESS_IDLE 0
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0

Wyświetl plik

@ -4,7 +4,7 @@ ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000C000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x1000; /* required amount of heap */
_Min_Heap_Size = 0x400; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */

Wyświetl plik

@ -0,0 +1,581 @@
/* ----------------------------------------------------------------------
* 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_interpolate_f32.c
*
* Description: FIR interpolation for floating-point sequences.
*
* 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"
/**
* @defgroup FIR_Interpolate Finite Impulse Response (FIR) Interpolator
*
* These functions combine an upsampler (zero stuffer) and an FIR filter.
* They are used in multirate systems for increasing the sample rate of a signal without introducing high frequency images.
* Conceptually, the functions are equivalent to the block diagram below:
* \image html FIRInterpolator.gif "Components included in the FIR Interpolator functions"
* After upsampling by a factor of <code>L</code>, the signal should be filtered by a lowpass filter with a normalized
* cutoff frequency of <code>1/L</code> in order to eliminate high frequency copies of the spectrum.
* The user of the function is responsible for providing the filter coefficients.
*
* The FIR interpolator functions provided in the CMSIS DSP Library combine the upsampler and FIR filter in an efficient manner.
* The upsampler inserts <code>L-1</code> zeros between each sample.
* Instead of multiplying by these zero values, the FIR filter is designed to skip them.
* This leads to an efficient implementation without any wasted effort.
* The functions operate on blocks of input and output data.
* <code>pSrc</code> points to an array of <code>blockSize</code> input values and
* <code>pDst</code> points to an array of <code>blockSize*L</code> output values.
*
* The library provides separate functions for Q15, Q31, and floating-point data types.
*
* \par Algorithm:
* The functions use a polyphase filter structure:
* <pre>
* y[n] = b[0] * x[n] + b[L] * x[n-1] + ... + b[L*(phaseLength-1)] * x[n-phaseLength+1]
* y[n+1] = b[1] * x[n] + b[L+1] * x[n-1] + ... + b[L*(phaseLength-1)+1] * x[n-phaseLength+1]
* ...
* y[n+(L-1)] = b[L-1] * x[n] + b[2*L-1] * x[n-1] + ....+ b[L*(phaseLength-1)+(L-1)] * x[n-phaseLength+1]
* </pre>
* This approach is more efficient than straightforward upsample-then-filter algorithms.
* With this method the computation is reduced by a factor of <code>1/L</code> when compared to using a standard FIR filter.
* \par
* <code>pCoeffs</code> points to a coefficient array of size <code>numTaps</code>.
* <code>numTaps</code> must be a multiple of the interpolation factor <code>L</code> and this is checked by the
* initialization functions.
* Internally, the function divides the FIR filter's impulse response into shorter filters of length
* <code>phaseLength=numTaps/L</code>.
* Coefficients are stored in time reversed order.
* \par
* <pre>
* {b[numTaps-1], b[numTaps-2], b[N-2], ..., b[1], b[0]}
* </pre>
* \par
* <code>pState</code> points to a state array of size <code>blockSize + phaseLength - 1</code>.
* Samples in the state buffer are stored in the order:
* \par
* <pre>
* {x[n-phaseLength+1], x[n-phaseLength], x[n-phaseLength-1], x[n-phaseLength-2]....x[0], x[1], ..., x[blockSize-1]}
* </pre>
* The state variables are updated after each block of data is processed, the coefficients are untouched.
*
* \par Instance Structure
* The coefficients and state variables for a filter are stored together in an instance data structure.
* A separate instance structure must be defined for each filter.
* Coefficient arrays may be shared among several instances while state variable array should be allocated separately.
* There are separate instance structure declarations for each of the 3 supported data types.
*
* \par Initialization Functions
* There is also an associated initialization function for each data type.
* The initialization function performs the following operations:
* - Sets the values of the internal structure fields.
* - Zeros out the values in the state buffer.
* - Checks to make sure that the length of the filter is a multiple of the interpolation factor.
* To do this manually without calling the init function, assign the follow subfields of the instance structure:
* L (interpolation factor), pCoeffs, phaseLength (numTaps / L), pState. Also set all of the values in pState to zero.
*
* \par
* Use of the initialization function is optional.
* However, if the initialization function is used, then the instance structure cannot be placed into a const data section.
* To place an instance structure into a const data section, the instance structure must be manually initialized.
* The code below statically initializes each of the 3 different data type filter instance structures
* <pre>
* arm_fir_interpolate_instance_f32 S = {L, phaseLength, pCoeffs, pState};
* arm_fir_interpolate_instance_q31 S = {L, phaseLength, pCoeffs, pState};
* arm_fir_interpolate_instance_q15 S = {L, phaseLength, pCoeffs, pState};
* </pre>
* where <code>L</code> is the interpolation factor; <code>phaseLength=numTaps/L</code> is the
* length of each of the shorter FIR filters used internally,
* <code>pCoeffs</code> is the address of the coefficient buffer;
* <code>pState</code> is the address of the state buffer.
* Be sure to set the values in the state buffer to zeros when doing static initialization.
*
* \par Fixed-Point Behavior
* Care must be taken when using the fixed-point versions of the FIR interpolate filter functions.
* In particular, the overflow and saturation behavior of the accumulator used in each function must be considered.
* Refer to the function specific documentation below for usage guidelines.
*/
/**
* @addtogroup FIR_Interpolate
* @{
*/
/**
* @brief Processing function for the floating-point FIR interpolator.
* @param[in] *S points to an instance of the floating-point FIR interpolator 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 input samples to process per call.
* @return none.
*/
#ifndef ARM_MATH_CM0_FAMILY
/* Run the below code for Cortex-M4 and Cortex-M3 */
void arm_fir_interpolate_f32(
const arm_fir_interpolate_instance_f32 * S,
float32_t * pSrc,
float32_t * pDst,
uint32_t blockSize)
{
float32_t *pState = S->pState; /* State pointer */
float32_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
float32_t *pStateCurnt; /* Points to the current sample of the state */
float32_t *ptr1, *ptr2; /* Temporary pointers for state and coefficient buffers */
float32_t sum0; /* Accumulators */
float32_t x0, c0; /* Temporary variables to hold state and coefficient values */
uint32_t i, blkCnt, j; /* Loop counters */
uint16_t phaseLen = S->phaseLength, tapCnt; /* Length of each polyphase filter component */
float32_t acc0, acc1, acc2, acc3;
float32_t x1, x2, x3;
uint32_t blkCntN4;
float32_t c1, c2, c3;
/* S->pState buffer contains previous frame (phaseLen - 1) samples */
/* pStateCurnt points to the location where the new input data should be written */
pStateCurnt = S->pState + (phaseLen - 1u);
/* Initialise blkCnt */
blkCnt = blockSize / 4;
blkCntN4 = blockSize - (4 * blkCnt);
/* Samples loop unrolled by 4 */
while(blkCnt > 0u)
{
/* Copy new input sample into the state buffer */
*pStateCurnt++ = *pSrc++;
*pStateCurnt++ = *pSrc++;
*pStateCurnt++ = *pSrc++;
*pStateCurnt++ = *pSrc++;
/* Address modifier index of coefficient buffer */
j = 1u;
/* Loop over the Interpolation factor. */
i = (S->L);
while(i > 0u)
{
/* Set accumulator to zero */
acc0 = 0.0f;
acc1 = 0.0f;
acc2 = 0.0f;
acc3 = 0.0f;
/* Initialize state pointer */
ptr1 = pState;
/* Initialize coefficient pointer */
ptr2 = pCoeffs + (S->L - j);
/* Loop over the polyPhase length. Unroll by a factor of 4.
** Repeat until we've computed numTaps-(4*S->L) coefficients. */
tapCnt = phaseLen >> 2u;
x0 = *(ptr1++);
x1 = *(ptr1++);
x2 = *(ptr1++);
while(tapCnt > 0u)
{
/* Read the input sample */
x3 = *(ptr1++);
/* Read the coefficient */
c0 = *(ptr2);
/* Perform the multiply-accumulate */
acc0 += x0 * c0;
acc1 += x1 * c0;
acc2 += x2 * c0;
acc3 += x3 * c0;
/* Read the coefficient */
c1 = *(ptr2 + S->L);
/* Read the input sample */
x0 = *(ptr1++);
/* Perform the multiply-accumulate */
acc0 += x1 * c1;
acc1 += x2 * c1;
acc2 += x3 * c1;
acc3 += x0 * c1;
/* Read the coefficient */
c2 = *(ptr2 + S->L * 2);
/* Read the input sample */
x1 = *(ptr1++);
/* Perform the multiply-accumulate */
acc0 += x2 * c2;
acc1 += x3 * c2;
acc2 += x0 * c2;
acc3 += x1 * c2;
/* Read the coefficient */
c3 = *(ptr2 + S->L * 3);
/* Read the input sample */
x2 = *(ptr1++);
/* Perform the multiply-accumulate */
acc0 += x3 * c3;
acc1 += x0 * c3;
acc2 += x1 * c3;
acc3 += x2 * c3;
/* Upsampling is done by stuffing L-1 zeros between each sample.
* So instead of multiplying zeros with coefficients,
* Increment the coefficient pointer by interpolation factor times. */
ptr2 += 4 * S->L;
/* Decrement the loop counter */
tapCnt--;
}
/* If the polyPhase length is not a multiple of 4, compute the remaining filter taps */
tapCnt = phaseLen % 0x4u;
while(tapCnt > 0u)
{
/* Read the input sample */
x3 = *(ptr1++);
/* Read the coefficient */
c0 = *(ptr2);
/* Perform the multiply-accumulate */
acc0 += x0 * c0;
acc1 += x1 * c0;
acc2 += x2 * c0;
acc3 += x3 * c0;
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* update states for next sample processing */
x0 = x1;
x1 = x2;
x2 = x3;
/* Decrement the loop counter */
tapCnt--;
}
/* The result is in the accumulator, store in the destination buffer. */
*pDst = acc0;
*(pDst + S->L) = acc1;
*(pDst + 2 * S->L) = acc2;
*(pDst + 3 * S->L) = acc3;
pDst++;
/* Increment the address modifier index of coefficient buffer */
j++;
/* Decrement the loop counter */
i--;
}
/* Advance the state pointer by 1
* to process the next group of interpolation factor number samples */
pState = pState + 4;
pDst += S->L * 3;
/* 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. */
while(blkCntN4 > 0u)
{
/* Copy new input sample into the state buffer */
*pStateCurnt++ = *pSrc++;
/* Address modifier index of coefficient buffer */
j = 1u;
/* Loop over the Interpolation factor. */
i = S->L;
while(i > 0u)
{
/* Set accumulator to zero */
sum0 = 0.0f;
/* Initialize state pointer */
ptr1 = pState;
/* Initialize coefficient pointer */
ptr2 = pCoeffs + (S->L - j);
/* Loop over the polyPhase length. Unroll by a factor of 4.
** Repeat until we've computed numTaps-(4*S->L) coefficients. */
tapCnt = phaseLen >> 2u;
while(tapCnt > 0u)
{
/* Read the coefficient */
c0 = *(ptr2);
/* Upsampling is done by stuffing L-1 zeros between each sample.
* So instead of multiplying zeros with coefficients,
* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Read the input sample */
x0 = *(ptr1++);
/* Perform the multiply-accumulate */
sum0 += x0 * c0;
/* Read the coefficient */
c0 = *(ptr2);
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Read the input sample */
x0 = *(ptr1++);
/* Perform the multiply-accumulate */
sum0 += x0 * c0;
/* Read the coefficient */
c0 = *(ptr2);
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Read the input sample */
x0 = *(ptr1++);
/* Perform the multiply-accumulate */
sum0 += x0 * c0;
/* Read the coefficient */
c0 = *(ptr2);
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Read the input sample */
x0 = *(ptr1++);
/* Perform the multiply-accumulate */
sum0 += x0 * c0;
/* Decrement the loop counter */
tapCnt--;
}
/* If the polyPhase length is not a multiple of 4, compute the remaining filter taps */
tapCnt = phaseLen % 0x4u;
while(tapCnt > 0u)
{
/* Perform the multiply-accumulate */
sum0 += *(ptr1++) * (*ptr2);
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Decrement the loop counter */
tapCnt--;
}
/* The result is in the accumulator, store in the destination buffer. */
*pDst++ = sum0;
/* Increment the address modifier index of coefficient buffer */
j++;
/* Decrement the loop counter */
i--;
}
/* Advance the state pointer by 1
* to process the next group of interpolation factor number samples */
pState = pState + 1;
/* Decrement the loop counter */
blkCntN4--;
}
/* Processing is complete.
** Now copy the last phaseLen - 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;
tapCnt = (phaseLen - 1u) >> 2u;
/* copy data */
while(tapCnt > 0u)
{
*pStateCurnt++ = *pState++;
*pStateCurnt++ = *pState++;
*pStateCurnt++ = *pState++;
*pStateCurnt++ = *pState++;
/* Decrement the loop counter */
tapCnt--;
}
tapCnt = (phaseLen - 1u) % 0x04u;
/* copy data */
while(tapCnt > 0u)
{
*pStateCurnt++ = *pState++;
/* Decrement the loop counter */
tapCnt--;
}
}
#else
/* Run the below code for Cortex-M0 */
void arm_fir_interpolate_f32(
const arm_fir_interpolate_instance_f32 * S,
float32_t * pSrc,
float32_t * pDst,
uint32_t blockSize)
{
float32_t *pState = S->pState; /* State pointer */
float32_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
float32_t *pStateCurnt; /* Points to the current sample of the state */
float32_t *ptr1, *ptr2; /* Temporary pointers for state and coefficient buffers */
float32_t sum; /* Accumulator */
uint32_t i, blkCnt; /* Loop counters */
uint16_t phaseLen = S->phaseLength, tapCnt; /* Length of each polyphase filter component */
/* S->pState buffer contains previous frame (phaseLen - 1) samples */
/* pStateCurnt points to the location where the new input data should be written */
pStateCurnt = S->pState + (phaseLen - 1u);
/* Total number of intput samples */
blkCnt = blockSize;
/* Loop over the blockSize. */
while(blkCnt > 0u)
{
/* Copy new input sample into the state buffer */
*pStateCurnt++ = *pSrc++;
/* Loop over the Interpolation factor. */
i = S->L;
while(i > 0u)
{
/* Set accumulator to zero */
sum = 0.0f;
/* Initialize state pointer */
ptr1 = pState;
/* Initialize coefficient pointer */
ptr2 = pCoeffs + (i - 1u);
/* Loop over the polyPhase length */
tapCnt = phaseLen;
while(tapCnt > 0u)
{
/* Perform the multiply-accumulate */
sum += *ptr1++ * *ptr2;
/* Increment the coefficient pointer by interpolation factor times. */
ptr2 += S->L;
/* Decrement the loop counter */
tapCnt--;
}
/* The result is in the accumulator, store in the destination buffer. */
*pDst++ = sum;
/* Decrement the loop counter */
i--;
}
/* Advance the state pointer by 1
* to process the next group of interpolation factor number samples */
pState = pState + 1;
/* Decrement the loop counter */
blkCnt--;
}
/* Processing is complete.
** Now copy the last phaseLen - 1 samples to the start 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;
tapCnt = phaseLen - 1u;
while(tapCnt > 0u)
{
*pStateCurnt++ = *pState++;
/* Decrement the loop counter */
tapCnt--;
}
}
#endif /* #ifndef ARM_MATH_CM0_FAMILY */
/**
* @} end of FIR_Interpolate group
*/

Wyświetl plik

@ -0,0 +1,121 @@
/*-----------------------------------------------------------------------------
* 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_interpolate_init_f32.c
*
* Description: Floating-point FIR interpolator 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_Interpolate
* @{
*/
/**
* @brief Initialization function for the floating-point FIR interpolator.
* @param[in,out] *S points to an instance of the floating-point FIR interpolator structure.
* @param[in] L upsample factor.
* @param[in] numTaps number of filter coefficients in the filter.
* @param[in] *pCoeffs points to the filter coefficient buffer.
* @param[in] *pState points to the state buffer.
* @param[in] blockSize number of input samples to process per call.
* @return The function returns ARM_MATH_SUCCESS if initialization was successful or ARM_MATH_LENGTH_ERROR if
* the filter length <code>numTaps</code> is not a multiple of the interpolation factor <code>L</code>.
*
* <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[numTaps-2], ..., b[1], b[0]}
* </pre>
* The length of the filter <code>numTaps</code> must be a multiple of the interpolation factor <code>L</code>.
* \par
* <code>pState</code> points to the array of state variables.
* <code>pState</code> is of length <code>(numTaps/L)+blockSize-1</code> words
* where <code>blockSize</code> is the number of input samples processed by each call to <code>arm_fir_interpolate_f32()</code>.
*/
arm_status arm_fir_interpolate_init_f32(
arm_fir_interpolate_instance_f32 * S,
uint8_t L,
uint16_t numTaps,
float32_t * pCoeffs,
float32_t * pState,
uint32_t blockSize)
{
arm_status status;
/* The filter length must be a multiple of the interpolation factor */
if((numTaps % L) != 0u)
{
/* Set status as ARM_MATH_LENGTH_ERROR */
status = ARM_MATH_LENGTH_ERROR;
}
else
{
/* Assign coefficient pointer */
S->pCoeffs = pCoeffs;
/* Assign Interpolation factor */
S->L = L;
/* Assign polyPhaseLength */
S->phaseLength = numTaps / L;
/* Clear state buffer and size of state array is always phaseLength + blockSize - 1 */
memset(pState, 0,
(blockSize +
((uint32_t) S->phaseLength - 1u)) * sizeof(float32_t));
/* Assign state pointer */
S->pState = pState;
status = ARM_MATH_SUCCESS;
}
return (status);
}
/**
* @} end of FIR_Interpolate group
*/

Wyświetl plik

@ -90,7 +90,7 @@ osThreadId ioEventTaskHandle;
uint32_t ioEventTaskBuffer[ 384 ];
osStaticThreadDef_t ioEventTaskControlBlock;
osThreadId audioInputTaskHandle;
uint32_t audioInputTaskBuffer[ 640 ];
uint32_t audioInputTaskBuffer[ 512 ];
osStaticThreadDef_t audioInputTaskControlBlock;
osThreadId modulatorTaskHandle;
uint32_t modulatorTaskBuffer[ 384 ];
@ -268,7 +268,7 @@ int main(void)
ioEventTaskHandle = osThreadCreate(osThread(ioEventTask), NULL);
/* definition and creation of audioInputTask */
osThreadStaticDef(audioInputTask, startAudioInputTask, osPriorityAboveNormal, 0, 640, audioInputTaskBuffer, &audioInputTaskControlBlock);
osThreadStaticDef(audioInputTask, startAudioInputTask, osPriorityAboveNormal, 0, 512, audioInputTaskBuffer, &audioInputTaskControlBlock);
audioInputTaskHandle = osThreadCreate(osThread(audioInputTask), NULL);
/* definition and creation of modulatorTask */
@ -401,12 +401,12 @@ void SystemClock_Config(void)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE|RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_8;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 40;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
@ -439,10 +439,10 @@ void SystemClock_Config(void)
PeriphClkInit.RngClockSelection = RCC_RNGCLKSOURCE_PLLSAI1;
PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_MSI;
PeriphClkInit.PLLSAI1.PLLSAI1M = 1;
PeriphClkInit.PLLSAI1.PLLSAI1N = 24;
PeriphClkInit.PLLSAI1.PLLSAI1N = 12;
PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7;
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2;
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV4;
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV4;
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_48M2CLK|RCC_PLLSAI1_ADC1CLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
@ -878,19 +878,19 @@ static void MX_DMA_Init(void)
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 5, 0);
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 5, 1);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 5, 0);
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 7, 1);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
/* DMA1_Channel3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 5, 0);
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 6, 1);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
/* DMA1_Channel6_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 5, 0);
HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
/* DMA1_Channel7_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 5, 0);
HAL_NVIC_SetPriority(DMA1_Channel7_IRQn, 6, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel7_IRQn);
/* DMA2_Channel4_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Channel4_IRQn, 5, 0);
@ -1066,6 +1066,12 @@ void assert_failed(uint8_t* file, uint32_t line)
}
#endif /* USE_FULL_ASSERT */
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName)
{
snprintf(error_message, sizeof(error_message), "stack overflow %s", pcTaskName);
while (1);
}
/**
* @}
*/

Wyświetl plik

@ -104,7 +104,7 @@ void AFSKTestTone::fill() const
case AFSKTestTone::State::SPACE:
if (kiss::settings().modem_type == kiss::Hardware::ModemType::M17)
{
getModulator().send(0x5F);
getModulator().send(0x22);
}
else
{

Wyświetl plik

@ -6,6 +6,7 @@
#include <array>
#include <algorithm>
#include <utility>
#include <cstdint>
namespace mobilinkd {

Wyświetl plik

@ -3,6 +3,7 @@
#include "HdlcFrame.hpp"
#include "Log.h"
#include "main.h"
#include "cmsis_os.h"
namespace mobilinkd { namespace tnc { namespace hdlc {

Wyświetl plik

@ -99,7 +99,7 @@ void startIOEventTask(void const*)
{
DEBUG("RF frame");
frame->source(frame->source() & 0x70);
if (!ioport->write(frame, 100))
if (!ioport->write(frame, frame->size() + 100))
{
ERROR("Timed out sending frame");
// The frame has been passed to the write() call. It owns it now.

Wyświetl plik

@ -13,7 +13,6 @@
#include "KissHardware.h"
#endif
#include <memory>
#include <array>
#include <cstdio>
#include <cstring>
@ -44,10 +43,10 @@ int powerOffViaUSB(void)
namespace mobilinkd { namespace tnc { namespace kiss {
#ifdef NUCLEOTNC
const char FIRMWARE_VERSION[] = "2.1.5";
const char FIRMWARE_VERSION[] = "2.1.8";
const char HARDWARE_VERSION[] = "Mobilinkd NucleoTNC";
#else
const char FIRMWARE_VERSION[] = "2.1.5";
const char FIRMWARE_VERSION[] = "2.1.8";
const char HARDWARE_VERSION[] = "Mobilinkd TNC3 2.1.1";
#endif
@ -502,7 +501,7 @@ void Hardware::handle_request(hdlc::IoFrame* frame)
case hardware::SET_DATETIME:
DEBUG("SET_DATETIME");
set_rtc_datetime(&*it);
set_rtc_datetime(static_cast<const uint8_t*>(&*it));
[[fallthrough]];
case hardware::GET_DATETIME:
DEBUG("GET_DATETIME");
@ -629,23 +628,22 @@ void Hardware::handle_ext_request(hdlc::IoFrame* frame) {
}
}
Hardware tmpHardware;
bool Hardware::load()
{
INFO("Loading settings from EEPROM");
auto tmp = std::make_unique<Hardware>();
memset(&tmpHardware, 0, sizeof(Hardware));
if (!tmp) return false;
memset(tmp.get(), 0, sizeof(Hardware));
if (!I2C_Storage::load(*tmp)) {
if (!I2C_Storage::load(tmpHardware)) {
ERROR("EEPROM read failed");
return false;
}
if (tmp->crc_ok())
if (tmpHardware.crc_ok())
{
memcpy(this, tmp.get(), sizeof(Hardware));
memcpy(this, &tmpHardware, sizeof(Hardware));
return true;
}
ERROR("EEPROM CRC error");

Wyświetl plik

@ -9,7 +9,6 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <array>
extern "C" void updatePtt(void);

Wyświetl plik

@ -12,7 +12,84 @@ const std::array<int16_t, FILTER_TAP_NUM> rrc_taps = {
17634, 18510, 18510, 17634, 15966, 13661, 10930, 8014, 5155, 2569, 427,
-1163, -2166, -2606, -2569, -2176, -1569, -886, -249, 253, 573, 698, 654,
485, 253, 16, -176, -293, -321, -269, -158, -19, 114, 216, 269, 267, 215,
129, 29, -62, -130, -163};
129, 29, -62, -130, -163
// -151, -100, -18, 80, 175, 246, 275, 249, 170, 49,
// -90, -219, -304, -318, -245, -88, 131, 373, 581, 695,
// 659, 437, 22, -556, -1229, -1890, -2409, -2641, -2452, -1738,
// -441, 1434, 3816, 6563, 9480, 12334, 14880, 16891, 18179, 18622,
// 18179, 16891, 14880, 12334, 9480, 6563, 3816, 1434, -441, -1738,
// -2452, -2641, -2409, -1890, -1229, -556, 22, 437, 659, 695,
// 581, 373, 131, -88, -245, -318, -304, -219, -90, 49,
// 170, 249, 275, 246, 175, 80, -18, -100, -151, 0
};
const std::array<int16_t, FILTER_TAP_NUM_9> rrc_taps_9 = {
33, -29, -93, -143, -165, -151, -100, -18, 80, 175, 246, 275,
249, 170, 49, -90, -219, -304, -318, -245, -88, 131, 373, 581,
695, 659, 437, 22, -556, -1229, -1890, -2409, -2641, -2452,
-1738, -441, 1434, 3816, 6563, 9480, 12334, 14880, 16891, 18179,
18622, 18179, 16891, 14880, 12334, 9480, 6563, 3816, 1434, -441,
-1738, -2452, -2641, -2409, -1890, -1229, -556, 22, 437, 659, 695,
581, 373, 131, -88, -245, -318, -304, -219, -90, 49, 170, 249,
275, 246, 175, 80, -18, -100, -151, -165, -143, -93, -29, 33, 0
};
const std::array<int16_t, FILTER_TAP_NUM_11> rrc_taps_11 = {
-92, -100, -87, -55, -10, 38, 81, 106, 107, 81,
33, -29, -93, -143, -165, -151, -100, -18, 80, 175,
246, 275, 249, 170, 49, -90, -219, -304, -318, -245,
-88, 131, 373, 581, 695, 659, 437, 22, -556, -1229,
-1890, -2409, -2641, -2452, -1738, -441, 1434, 3816, 6563, 9480,
12334, 14880, 16891, 18179, 18622, 18179, 16891, 14880, 12334, 9480,
6563, 3816, 1434, -441, -1738, -2452, -2641, -2409, -1890, -1229,
-556, 22, 437, 659, 695, 581, 373, 131, -88, -245,
-318, -304, -219, -90, 49, 170, 249, 275, 246, 175,
80, -18, -100, -151, -165, -143, -93, -29, 33, 81,
107, 106, 81, 38, -10, -55, -87, -100, -92, 0
};
const std::array<int16_t, FILTER_TAP_NUM_15> rrc_taps_15 = {
48, 51, 44, 27, 3, -21, -41, -53, -53, -40,
-16, 12, 41, 63, 72, 66, 43, 9, -30, -67,
-92, -100, -87, -55, -10, 38, 81, 106, 107, 81,
33, -29, -93, -143, -165, -151, -100, -18, 80, 175,
246, 275, 249, 170, 49, -90, -219, -304, -318, -245,
-88, 131, 373, 581, 695, 659, 437, 22, -556, -1229,
-1890, -2409, -2641, -2452, -1738, -441, 1434, 3816, 6563, 9480,
12334, 14880, 16891, 18179, 18622, 18179, 16891, 14880, 12334, 9480,
6563, 3816, 1434, -441, -1738, -2452, -2641, -2409, -1890, -1229,
-556, 22, 437, 659, 695, 581, 373, 131, -88, -245,
-318, -304, -219, -90, 49, 170, 249, 275, 246, 175,
80, -18, -100, -151, -165, -143, -93, -29, 33, 81,
107, 106, 81, 38, -10, -55, -87, -100, -92, -67,
-30, 9, 43, 66, 72, 63, 41, 12, -16, -40,
-53, -53, -41, -21, 3, 27, 44, 51, 48, 0
};
const std::array<int16_t, FILTER_TAP_NUM_21> rrc_taps_21 = {
-1, 1, 3, 5, 6, 5, 3, 0, -2, -5, -7, -7, -6, -4, 0, 3, 6, 8,
7, 6, 2, -1, -5, -8, -10, -9, -6, -1, 3, 8, 12, 12, 11, 6, 0,
-5, -10, -13, -13, -10, -4, 3, 10, 15, 18, 16, 10, 2, -7, -16,
-23, -25, -21, -13, -2, 9, 20, 26, 26, 20, 8, -7, -23, -35, -41,
-37, -25, -4, 20, 43, 61, 68, 62, 42, 12, -22, -54, -76, -79,
-61, -22, 32, 93, 145, 173, 164, 109, 5, -139, -307, -472, -602,
-660, -613, -434, -110, 358, 954, 1640, 2370, 3083, 3720, 4222,
4544, 4655, 4544, 4222, 3720, 3083, 2370, 1640, 954, 358, -110,
-434, -613, -660, -602, -472, -307, -139, 5, 109, 164, 173, 145,
93, 32, -22, -61, -79, -76, -54, -22, 12, 42, 62, 68, 61, 43, 20,
-4, -25, -37, -41, -35, -23, -7, 8, 20, 26, 26, 20, 9, -2, -13,
-21, -25, -23, -16, -7, 2, 10, 16, 18, 15, 10, 3, -4, -10, -13,
-13, -10, -5, 0, 6, 11, 12, 12, 8, 3, -1, -6, -9, -10, -8, -5,
-1, 2, 6, 7, 8, 6, 3, 0, -4, -6, -7, -7, -5, -2, 0, 3, 5, 6, 5,
3, 1, -1, 0
};
const std::array<float, FILTER_TAP_NUM_9> rrc_taps_f9 = {
0.002017321931508163, -0.0018256054303579805, -0.00571615173291049, -0.008746639552588416, -0.010105075751866371, -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525, -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935, 0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876, 0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885, -0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504, -0.10610329539459686, -0.026921412469634916, 0.08757875030779196, 0.23293327870303457, 0.4006012210123992, 0.5786324696325503, 0.7528286479934068, 0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815, 1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503, 0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916, -0.10610329539459686, -0.14969512896336504, -0.16119995609538576, -0.14703962203941534, -0.11540977897637611, -0.07502635967975885, -0.03394615682795165, 0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876, 0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746, -0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165, 0.003031522725559901, 0.01042830577908502, 0.015256245142156299, 0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491, -0.001125978562075172, -0.006136551625729697, -0.009265784007800534, -0.010105075751866371, -0.008746639552588416, -0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0
};
const std::array<float, FILTER_TAP_NUM_15> rrc_taps_f15 = {
0.0029364388513841593, 0.0031468394550958484, 0.002699564567597445, 0.001661182944400927, 0.00023319405581230247, -0.0012851320781224025, -0.0025577136087664687, -0.0032843366522956313, -0.0032697038088887226, -0.0024733964729590865, -0.0010285696910973807, 0.0007766690889758685, 0.002553421969211845, 0.0038920145144327816, 0.004451886520053017, 0.00404219185231544, 0.002674727068399207, 0.0005756567993179152, -0.0018493784971116507, -0.004092346891623224, -0.005648131453822014, -0.006126925416243605, -0.005349511529163396, -0.003403189203405097, -0.0006430502751187517, 0.002365929161655135, 0.004957956568090113, 0.006506845894531803, 0.006569574194782443, 0.0050017573119839134, 0.002017321931508163, -0.0018256054303579805, -0.00571615173291049, -0.008746639552588416, -0.010105075751866371, -0.009265784007800534, -0.006136551625729697, -0.001125978562075172, 0.004891777252042491, 0.01071805138282269, 0.01505751553351295, 0.01679337935001369, 0.015256245142156299, 0.01042830577908502, 0.003031522725559901, -0.0055333532968188165, -0.013403099825723372, -0.018598682349642525, -0.01944761739590459, -0.015005271935951746, -0.0053887880354343935, 0.008056525910253532, 0.022816244158307273, 0.035513467692208076, 0.04244131815783876, 0.04025481153629372, 0.02671818654865632, 0.0013810216516704976, -0.03394615682795165, -0.07502635967975885, -0.11540977897637611, -0.14703962203941534, -0.16119995609538576, -0.14969512896336504, -0.10610329539459686, -0.026921412469634916, 0.08757875030779196, 0.23293327870303457, 0.4006012210123992, 0.5786324696325503, 0.7528286479934068, 0.908262741447522, 1.0309661131633199, 1.1095611856548013, 1.1366197723675815, 1.1095611856548013, 1.0309661131633199, 0.908262741447522, 0.7528286479934068, 0.5786324696325503, 0.4006012210123992, 0.23293327870303457, 0.08757875030779196, -0.026921412469634916, -0.10610329539459686, -0.14969512896336504, -0.16119995609538576, -0.14703962203941534, -0.11540977897637611, -0.07502635967975885, -0.03394615682795165, 0.0013810216516704976, 0.02671818654865632, 0.04025481153629372, 0.04244131815783876, 0.035513467692208076, 0.022816244158307273, 0.008056525910253532, -0.0053887880354343935, -0.015005271935951746, -0.01944761739590459, -0.018598682349642525, -0.013403099825723372, -0.0055333532968188165, 0.003031522725559901, 0.01042830577908502, 0.015256245142156299, 0.01679337935001369, 0.01505751553351295, 0.01071805138282269, 0.004891777252042491, -0.001125978562075172, -0.006136551625729697, -0.009265784007800534, -0.010105075751866371, -0.008746639552588416, -0.00571615173291049, -0.0018256054303579805, 0.002017321931508163, 0.0050017573119839134, 0.006569574194782443, 0.006506845894531803, 0.004957956568090113, 0.002365929161655135, -0.0006430502751187517, -0.003403189203405097, -0.005349511529163396, -0.006126925416243605, -0.005648131453822014, -0.004092346891623224, -0.0018493784971116507, 0.0005756567993179152, 0.002674727068399207, 0.00404219185231544, 0.004451886520053017, 0.0038920145144327816, 0.002553421969211845, 0.0007766690889758685, -0.0010285696910973807, -0.0024733964729590865, -0.0032697038088887226, -0.0032843366522956313, -0.0025577136087664687, -0.0012851320781224025, 0.00023319405581230247, 0.001661182944400927, 0.002699564567597445, 0.0031468394550958484, 0.0029364388513841593, 0.0
};
}} // mobilinkd::m17

Wyświetl plik

@ -5,12 +5,25 @@
#include <array>
#include <cstdint>
#include <cstddef>
namespace mobilinkd { namespace m17 {
constexpr size_t FILTER_TAP_NUM = 80;
constexpr size_t FILTER_TAP_NUM_9 = 90;
constexpr size_t FILTER_TAP_NUM_11 = 110;
constexpr size_t FILTER_TAP_NUM_15 = 150;
constexpr size_t FILTER_TAP_NUM_21 = 210;
extern const std::array<int16_t, FILTER_TAP_NUM> rrc_taps;
extern const std::array<int16_t, FILTER_TAP_NUM_9> rrc_taps_9;
extern const std::array<int16_t, FILTER_TAP_NUM_11> rrc_taps_11;
extern const std::array<int16_t, FILTER_TAP_NUM_15> rrc_taps_15;
extern const std::array<int16_t, FILTER_TAP_NUM_21> rrc_taps_21;
extern const std::array<float, FILTER_TAP_NUM_9> rrc_taps_f9;
extern const std::array<float, FILTER_TAP_NUM_15> rrc_taps_f15;
constexpr std::array<uint8_t, 2> LSF_SYNC = { 0x55, 0xF7 };
constexpr std::array<uint8_t, 2> STREAM_SYNC = { 0xFF, 0x5D };
constexpr std::array<uint8_t, 2> PACKET_SYNC = { 0x75, 0xFF };

Wyświetl plik

@ -16,7 +16,7 @@ void M17Demodulator::start()
{
SysClock80();
demod_filter.init(m17::rrc_taps.data());
demod_filter.init(m17::rrc_taps_9.data());
passall(kiss::settings().options & KISS_OPTION_PASSALL);
hadc1.Init.OversamplingMode = DISABLE;
@ -36,7 +36,7 @@ void M17Demodulator::start()
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
CxxErrorHandler();
startADC(1666, ADC_BLOCK_SIZE);
startADC(1665, ADC_BLOCK_SIZE);
// getModulator().start_loopback();
}

Wyświetl plik

@ -43,12 +43,12 @@ struct M17Demodulator : IDemodulator
static constexpr uint32_t SAMPLE_RATE = 48000;
static constexpr uint16_t VREF = 4095;
using audio_filter_t = Q15FirFilter<ADC_BLOCK_SIZE, m17::FILTER_TAP_NUM>;
using audio_filter_t = Q15FirFilter<ADC_BLOCK_SIZE, m17::FILTER_TAP_NUM_9>;
using demod_result_t = std::tuple<float, float, int, float>;
enum class DemodState { UNLOCKED, LSF_SYNC, FRAME_SYNC, FRAME };
audio_filter_t demod_filter{m17::rrc_taps.data()};
audio_filter_t demod_filter{m17::rrc_taps_9.data()};
const float sample_rate = 48000.0;
const float symbol_rate = 4800.0;
float gain = 0.01;
@ -109,6 +109,7 @@ struct M17Demodulator : IDemodulator
decoder.passall(enabled);
}
[[gnu::noinline]]
void frame(demod_result_t demod_result, hdlc::IoFrame*& result)
{
auto [sample, phase, symbol, evm] = demod_result;
@ -186,8 +187,9 @@ struct M17Demodulator : IDemodulator
sync_count = 0;
std::copy(tmp, tmp + len, buffer.begin());
auto valid = decoder(sync_word_type, buffer, result, ber);
if (!valid)
switch (valid)
{
case M17FrameDecoder::DecodeResult::FAIL:
WARN("decode invalid");
if (result && !passall_)
{
@ -195,10 +197,13 @@ struct M17Demodulator : IDemodulator
if (result) hdlc::release(result);
result = 0;
}
}
else
{
break;
case M17FrameDecoder::DecodeResult::EOS:
demodState = DemodState::LSF_SYNC;
break;
case M17FrameDecoder::DecodeResult::OK:
INFO("valid frame for sync word type %d", int(sync_word_type));
break;
}
}
}
@ -214,6 +219,7 @@ struct M17Demodulator : IDemodulator
}
}
[[gnu::noinline]]
demod_result_t demod()
{
int16_t polarity = kiss::settings().rx_rev_polarity() ? -1 : 1;

Wyświetl plik

@ -78,7 +78,7 @@ void M17Encoder::run()
osThreadResume(encoderTaskHandle);
auto start = osKernelSysTick();
uint32_t delay_ms = 0;
int32_t delay_ms = 0;
state = State::IDLE;
@ -118,14 +118,23 @@ void M17Encoder::run()
}
evt = osMessagePeek(input_queue, delay_ms);
auto msgs = osMessageWaiting(input_queue);
// auto num_msgs = osMessageWaiting(input_queue);
back2back = (evt.status == osEventMessage);
if (!back2back)
{
if (state != State::IDLE)
{
state = State::IDLE;
WARN("Timed out waiting for packet (%lums).", delay_ms);
size_t counter = 5;
do {
send_link_setup();
evt = osMessagePeek(input_queue, 40);
} while (evt.status != osEventMessage && --counter != 0);
if (evt.status != osEventMessage) {
state = State::IDLE;
WARN("Timed out waiting for packet.");
}
}
delay_ms = 0;
}
@ -235,16 +244,16 @@ void M17Encoder::send_preamble()
void M17Encoder::send_link_setup()
{
punctured.fill(0);
m17_frame.fill(0);
auto frame = tnc::hdlc::acquire_wait();
// Encoder, puncture, interleave & randomize.
auto encoded = conv_encode(current_lsf);
puncture_bytes(encoded, punctured, P1);
interleaver.interleave(punctured);
randomizer(punctured);
puncture_bytes(encoded, m17_frame, P1);
interleaver.interleave(m17_frame);
randomizer(m17_frame);
for (auto c : m17::LSF_SYNC) frame->push_back(c);
for (auto c : punctured) frame->push_back(c);
for (auto c : m17_frame) frame->push_back(c);
auto status = osMessagePut(
m17EncoderInputQueueHandle,
@ -253,7 +262,7 @@ void M17Encoder::send_link_setup()
if (status != osOK)
{
tnc::hdlc::release(frame);
WARN("M17 failed to send preamble");
WARN("M17 failed to send LSF");
}
}
@ -306,18 +315,16 @@ void M17Encoder::send_full_packet(tnc::hdlc::IoFrame* frame)
void M17Encoder::send_packet_frame(const std::array<uint8_t, 26>& packet_frame)
{
frame_t punctured;
// Encoder, puncture, interleave & randomize.
auto encoded = conv_encode(packet_frame, 206);
puncture_bytes(encoded, punctured, P3);
interleaver.interleave(punctured);
randomizer(punctured);
puncture_bytes(encoded, m17_frame, P3);
interleaver.interleave(m17_frame);
randomizer(m17_frame);
auto frame = tnc::hdlc::acquire_wait();
for (auto c : m17::PACKET_SYNC) frame->push_back(c);
for (auto c : punctured) frame->push_back(c);
for (auto c : m17_frame) frame->push_back(c);
auto status = osMessagePut(
m17EncoderInputQueueHandle,
@ -332,9 +339,6 @@ void M17Encoder::send_packet_frame(const std::array<uint8_t, 26>& packet_frame)
void M17Encoder::send_stream(tnc::hdlc::IoFrame* frame, FrameType)
{
payload_t punctured; // Punctured audio payload (272 bits, 34 bytes).
frame_t m17_frame;
// Construct LICH.
auto it = frame->begin();
std::advance(it, 6);
@ -347,9 +351,11 @@ void M17Encoder::send_stream(tnc::hdlc::IoFrame* frame, FrameType)
std::array<uint8_t, 20> data;
std::copy(it, frame->end(), data.begin());
if (data[0] & 0x80) state = State::IDLE; // EOS
auto encoded = conv_encode(data);
puncture_bytes(encoded, punctured, P2);
std::copy(punctured.begin(), punctured.end(), fit); // Copy after LICH.
puncture_bytes(encoded, stream_payload, P2);
std::copy(stream_payload.begin(), stream_payload.end(), fit); // Copy after LICH.
interleaver.interleave(m17_frame); // Interleave entire frame.
randomizer(m17_frame); // Randomize entire frame.
@ -409,6 +415,7 @@ void M17Encoder::create_link_setup(tnc::hdlc::IoFrame* frame, FrameType type)
/**
* Encode each LSF segment into a Golay-encoded LICH segment bitstream.
*/
[[gnu::noinline]]
M17Encoder::lich_segment_t M17Encoder::make_lich_segment(
std::array<uint8_t, 6> segment)
{
@ -511,6 +518,7 @@ void M17Encoder::encoderTask(void const*)
{
osEvent evt = osMessageGet(m17EncoderInputQueueHandle, osWaitForever);
if (evt.status != osEventMessage) continue;
auto frame = static_cast<IoFrame*>(evt.value.p);
if (frame->size() != 48) WARN("Bad frame size %u", frame->size());

Wyświetl plik

@ -189,7 +189,8 @@ private:
osThreadId encoderTaskHandle;
State state = State::INACTIVE;
frame_t punctured;
frame_t m17_frame;
payload_t stream_payload;
lsf_t current_lsf;
LinkSetupFrame::encoded_call_t src;
LinkSetupFrame::encoded_call_t dest;

Wyświetl plik

@ -152,8 +152,10 @@ struct M17FrameDecoder
Viterbi<decltype(trellis_), 4> viterbi_{trellis_};
CRC16<0x5935, 0xFFFF> crc_;
enum class State {LSF, STREAM, BASIC_PACKET, FULL_PACKET};
enum class SyncWordType { LSF, STREAM, PACKET, RESERVED };
enum class DecodeResult { FAIL, OK, EOS };
State state_ = State::LSF;
@ -172,6 +174,7 @@ struct M17FrameDecoder
audio_callback_t audio_callback_;
union
{
std::array<uint8_t, 30> lich;
std::array<uint8_t, 240> lsf;
std::array<uint8_t, 206> packet;
std::array<uint8_t, 160> stream;
@ -261,7 +264,8 @@ struct M17FrameDecoder
* @param ber
* @return
*/
bool decode_lsf(buffer_t& buffer, tnc::hdlc::IoFrame*& lsf, int& ber)
[[gnu::noinline]]
DecodeResult decode_lsf(buffer_t& buffer, tnc::hdlc::IoFrame*& lsf, int& ber)
{
depuncture(buffer, tmp.lsf, P1);
ber = viterbi_.decode(tmp.lsf, output.lsf);
@ -282,16 +286,16 @@ struct M17FrameDecoder
lsf->push_back(0);
lsf->push_back(0);
lsf->source(0x20);
return true;
return DecodeResult::OK;
}
lsf = nullptr;
return true;
return DecodeResult::OK;
}
else
{
lich_segments = 0;
output.lsf.fill(0);
return false;
return DecodeResult::FAIL;
}
}
@ -315,7 +319,7 @@ struct M17FrameDecoder
return false;
}
decoded >>= 12; // Remove check bits and parity.
INFO("Golay decode good for %08lx (%du)", decoded, i);
DEBUG("Golay decode good for %08lx (%du)", decoded, i);
// append codeword.
if (i & 1)
{
@ -331,47 +335,51 @@ struct M17FrameDecoder
return true;
}
bool decode_lich(buffer_t& buffer, tnc::hdlc::IoFrame*& lsf, int& ber)
[[gnu::noinline]]
DecodeResult decode_lich(buffer_t& buffer, tnc::hdlc::IoFrame*& lsf, int& ber)
{
tmp.lich.fill(0);
// Read the 4 12-bit codewords from LICH into buffers.lich.
if (!unpack_lich(buffer)) return false;
if (!unpack_lich(buffer)) return DecodeResult::FAIL;
uint8_t fragment_number = tmp.lich[5]; // Get fragment number.
fragment_number = (fragment_number >> 5) & 7;
// Copy decoded LICH to superframe buffer.
std::copy(tmp.lich.begin(), tmp.lich.begin() + 5,
output.lsf.begin() + (fragment_number * 5));
output.lich.begin() + (fragment_number * 5));
lich_segments |= (1 << fragment_number); // Indicate segment received.
INFO("got segment %d, have %02x", int(fragment_number), int(lich_segments));
if (lich_segments != 0x3F) return false; // More to go...
if (lich_segments != 0x3F) return DecodeResult::FAIL; // More to go...
detail::to_bytes(output.lsf, current_lsf);
crc_.reset();
for (auto c : current_lsf) crc_(c);
for (auto c : output.lich) crc_(c);
auto checksum = crc_.get();
INFO("LICH crc = %04x", checksum);
if (checksum == 0)
{
state_ = State::STREAM;
lsf = tnc::hdlc::acquire_wait();
for (auto c : current_lsf) lsf->push_back(c);
for (auto c : output.lich) lsf->push_back(c);
lsf->push_back(0);
lsf->push_back(0);
lsf->source(0x20);
ber = 0;
return true;
return DecodeResult::OK;
}
#ifdef KISS_LOGGING
dump(output.lich);
#endif
// Failed CRC... try again.
lich_segments = 0;
output.lsf.fill(0);
output.lich.fill(0);
ber = 128;
return false;
return DecodeResult::FAIL;
}
bool decode_stream(buffer_t& buffer, tnc::hdlc::IoFrame*& stream, int& ber)
[[gnu::noinline]]
DecodeResult decode_stream(buffer_t& buffer, tnc::hdlc::IoFrame*& stream, int& ber)
{
std::array<uint8_t, 20> stream_segment;
@ -391,10 +399,15 @@ struct M17FrameDecoder
for (auto c : stream_segment) crc_(c);
auto checksum = crc_.get();
INFO("crc = %04x", checksum); // Otherwise ignored.
if ((checksum == 0) && (stream_segment[0] & 0x80))
{
INFO("EOS");
state_ = State::LSF;
}
stream->push_back(0);
stream->push_back(0);
stream->source(0x20);
return true;
return state_ == State::LSF ? DecodeResult::EOS : DecodeResult::OK;
}
/**
@ -408,7 +421,8 @@ struct M17FrameDecoder
* @param ber the estimated BER (really more SNR) of the packet.
* @return true if a valid packet is returned, otherwise false.
*/
bool decode_basic_packet(buffer_t& buffer, tnc::hdlc::IoFrame*& packet, int& ber)
[[gnu::noinline]]
DecodeResult decode_basic_packet(buffer_t& buffer, tnc::hdlc::IoFrame*& packet, int& ber)
{
std::array<uint8_t, 26> packet_segment;
@ -439,7 +453,7 @@ struct M17FrameDecoder
current_packet->source(0);
packet = current_packet;
current_packet = nullptr;
return true;
return DecodeResult::OK;
}
WARN("packet bad fcs = %04x, crc = %04x", current_packet->fcs(), current_packet->crc());
if (passall_)
@ -452,7 +466,7 @@ struct M17FrameDecoder
tnc::hdlc::release(current_packet);
current_packet = nullptr;
}
return false;
return DecodeResult::FAIL;
}
size_t frame_number = (packet_segment[25] & 0x7F) >> 2;
@ -467,7 +481,7 @@ struct M17FrameDecoder
}
packet = nullptr;
return true;
return DecodeResult::OK;
}
/**
@ -481,7 +495,8 @@ struct M17FrameDecoder
* @param ber
* @return
*/
bool decode_full_packet(buffer_t& buffer, tnc::hdlc::IoFrame*& packet, int& ber)
[[gnu::noinline]]
DecodeResult decode_full_packet(buffer_t& buffer, tnc::hdlc::IoFrame*& packet, int& ber)
{
std::array<uint8_t, 26> packet_segment;
@ -510,7 +525,7 @@ struct M17FrameDecoder
current_packet = nullptr;
packet_frame_counter = 0;
state_ = State::LSF;
return true;
return DecodeResult::OK;
}
size_t frame_number = (packet_segment[25] & 0x7F) >> 2;
@ -525,7 +540,7 @@ struct M17FrameDecoder
}
packet = nullptr;
return true;
return DecodeResult::OK;
}
/**
@ -564,7 +579,8 @@ struct M17FrameDecoder
* - FULL_PACKET when any packet frame is received.
* - LSF when the EOS indicator is set, or when a stream frame is received.
*/
bool operator()(SyncWordType frame_type, buffer_t& buffer,
[[gnu::noinline]]
DecodeResult operator()(SyncWordType frame_type, buffer_t& buffer,
tnc::hdlc::IoFrame*& result, int& ber)
{
derandomize_(buffer);
@ -603,7 +619,7 @@ struct M17FrameDecoder
break;
}
return false;
return DecodeResult::FAIL;
}
};

Wyświetl plik

@ -14,7 +14,8 @@ void M17Modulator::init(const kiss::Hardware& hw)
SysClock80();
// Configure 80MHz clock for 48kHz.
htim7.Init.Period = 1666;
htim7.Init.Period = 1665;
htim7.Init.Prescaler = 0;
if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
{
ERROR("htim7 init failed");

Wyświetl plik

@ -26,29 +26,30 @@ struct M17Modulator : Modulator
// Six buffers per M17 frame, or 12 half-buffer interrupts.
static constexpr uint8_t UPSAMPLE = 10;
static constexpr uint32_t BLOCKSIZE = 4;
static constexpr uint32_t STATE_SIZE = (m17::FILTER_TAP_NUM / UPSAMPLE) + BLOCKSIZE - 1;
static constexpr uint32_t STATE_SIZE = (m17::FILTER_TAP_NUM_15 / UPSAMPLE) + BLOCKSIZE - 1;
static constexpr int16_t DAC_BUFFER_LEN = 80; // 8 symbols, 16 bits, 2 bytes.
static constexpr int16_t TRANSFER_LEN = DAC_BUFFER_LEN / 2; // 4 symbols, 8 bits, 1 byte.
static constexpr uint16_t VREF = 4095;
enum class State { STOPPED, STARTING, RUNNING, STOPPING };
arm_fir_interpolate_instance_q15 fir_interpolator;
std::array<q15_t, STATE_SIZE> fir_state;
arm_fir_interpolate_instance_f32 fir_interpolator;
std::array<float, STATE_SIZE> fir_state;
std::array<int16_t, DAC_BUFFER_LEN> buffer_;
std::array<int16_t, 4> symbols;
std::array<float, 4> symbols;
osMessageQId dacOutputQueueHandle_{0};
PTT* ptt_{nullptr};
uint16_t volume_{4096};
volatile uint16_t delay_count = 0; // TX Delay
volatile uint16_t stop_count = 0; // Flush the RRC matched filter.
State state{State::STOPPED};
float tmp[TRANSFER_LEN];
M17Modulator(osMessageQId queue, PTT* ptt)
: dacOutputQueueHandle_(queue), ptt_(ptt)
{
arm_fir_interpolate_init_q15(
&fir_interpolator, UPSAMPLE, m17::FILTER_TAP_NUM,
(q15_t*) m17::rrc_taps.data(), fir_state.data(), BLOCKSIZE);
arm_fir_interpolate_init_f32(
&fir_interpolator, UPSAMPLE, m17::FILTER_TAP_NUM_15,
(float32_t*) m17::rrc_taps_f15.data(), fir_state.data(), BLOCKSIZE);
}
~M17Modulator() override {}
@ -96,6 +97,7 @@ struct M17Modulator : Modulator
void send(uint8_t bits) override
{
uint16_t txdelay = 0;
switch (state)
{
case State::STOPPING:
@ -115,7 +117,7 @@ struct M17Modulator : Modulator
start_conversion();
ptt_->on();
while (delay_count < txdelay) osThreadYield();
stop_count = 2; // 8 symbols to flush the RRC filter.
stop_count = 4; // 16 symbols to flush the RRC filter.
osMessagePut(dacOutputQueueHandle_, bits, osWaitForever);
state = State::RUNNING;
break;
@ -126,12 +128,13 @@ struct M17Modulator : Modulator
}
// DAC DMA interrupt functions.
[[gnu::noinline]]
void fill_first(uint8_t bits) override
{
fill(buffer_.data(), bits);
}
[[gnu::noinline]]
void fill_last(uint8_t bits) override
{
fill(buffer_.data() + TRANSFER_LEN, bits);
@ -256,11 +259,13 @@ private:
DAC_ALIGN_12B_R);
}
uint16_t adjust_level(int32_t sample) const
uint16_t adjust_level(float sample) const
{
sample *= volume_;
sample >>= 12;
sample /= 8;
sample += 2048;
if (sample > 4095) sample = 4095;
else if (sample < 0) sample = 0;
return sample;
}
@ -276,35 +281,51 @@ private:
return 0;
}
constexpr int16_t symbol_skew(int32_t symbol)
{
const size_t shift = 8;
if (symbol == 1 || symbol == -1)
{
int32_t offset = ((symbol * (kiss::settings().tx_twist - 50)) << shift) / 50;
return (symbol << shift) - offset;
}
else
{
return symbol << shift;
}
}
[[gnu::noinline]]
void fill(int16_t* buffer, uint8_t bits)
{
int16_t polarity = kiss::settings().tx_rev_polarity() ? -1 : 1;
for (size_t i = 0; i != 4; ++i)
{
symbols[i] = (bits_to_symbol(bits >> 6) << 10) * polarity;
symbols[i] = bits_to_symbol(bits >> 6) * polarity;
bits <<= 2;
}
arm_fir_interpolate_q15(
&fir_interpolator, symbols.data(), buffer, BLOCKSIZE);
arm_fir_interpolate_f32(
&fir_interpolator, symbols.data(), tmp, BLOCKSIZE);
for (size_t i = 0; i != TRANSFER_LEN; ++i)
{
buffer[i] = adjust_level(buffer[i]);
buffer[i] = adjust_level(tmp[i]);
}
}
[[gnu::noinline]]
void fill_empty(int16_t* buffer)
{
symbols.fill(0);
arm_fir_interpolate_q15(
&fir_interpolator, symbols.data(), buffer, BLOCKSIZE);
arm_fir_interpolate_f32(
&fir_interpolator, symbols.data(), tmp, BLOCKSIZE);
for (size_t i = 0; i != TRANSFER_LEN; ++i)
{
buffer[i] = adjust_level(buffer[i]);
buffer[i] = adjust_level(tmp[i]);
}
}
};

Wyświetl plik

@ -6,7 +6,9 @@
#include "Convolution.h"
#include "Util.h"
#include <array>
#include <limits>
#include <span>
namespace mobilinkd
{
@ -107,27 +109,62 @@ struct Viterbi
state_transition_t nextState_;
state_transition_t prevState_;
metrics_t prevMetrics, currMetrics;
std::array<std::bitset<NumStates>, 244> history_storage_;
Viterbi(Trellis_ trellis)
: cost_(makeCost<Trellis_, LLR_>(trellis))
, nextState_(makeNextState(trellis))
, prevState_(makePrevState(trellis))
{}
[[gnu::noinline]]
void calculate_path_metric(
const std::array<int16_t, NumStates / 2>& cost0,
const std::array<int16_t, NumStates / 2>& cost1,
std::bitset<NumStates>& hist,
size_t j
) {
auto& i0 = nextState_[j][0];
auto& i1 = nextState_[j][1];
int16_t c0 = cost0[j];
int16_t c1 = cost1[j];
auto& p0 = prevMetrics[j];
auto& p1 = prevMetrics[j + NumStates / 2];
int32_t m0 = p0 + c0;
int32_t m1 = p0 + c1;
int32_t m2 = p1 + c1;
int32_t m3 = p1 + c0;
bool d0 = m0 > m2;
bool d1 = m1 > m3;
hist.set(i0, d0);
hist.set(i1, d1);
currMetrics[i0] = d0 ? m2 : m0;
currMetrics[i1] = d1 ? m3 : m1;
}
/**
* Viterbi soft decoder using LLR inputs where 0 == erasure.
*
* @return path metric for estimating BER.
*/
template <size_t IN, size_t OUT>
size_t decode(std::array<int8_t, IN> in, std::array<uint8_t, OUT>& out)
size_t decode(std::array<int8_t, IN> const& in, std::array<uint8_t, OUT>& out)
{
static_assert(sizeof(history_storage_) >= IN / 2);
constexpr auto MAX_METRIC = std::numeric_limits<typename metrics_t::value_type>::max() / 2;
metrics_t prevMetrics, currMetrics;
prevMetrics.fill(MAX_METRIC);
prevMetrics[0] = 0; // Starting point.
std::array<std::bitset<NumStates>, IN / 2> history;
std::span history(history_storage_.begin(), history_storage_.begin() + IN / 2);
constexpr size_t BUTTERFLY_SIZE = NumStates / 2;
@ -150,27 +187,7 @@ struct Viterbi
for (size_t j = 0; j != BUTTERFLY_SIZE; ++j)
{
auto& i0 = nextState_[j][0];
auto& i1 = nextState_[j][1];
int16_t c0 = cost0[j];
int16_t c1 = cost1[j];
auto& p0 = prevMetrics[j];
auto& p1 = prevMetrics[j + BUTTERFLY_SIZE];
int32_t m0 = p0 + c0;
int32_t m1 = p0 + c1;
int32_t m2 = p1 + c1;
int32_t m3 = p1 + c0;
bool d0 = m0 > m2;
bool d1 = m1 > m3;
hist.set(i0, d0);
hist.set(i1, d1);
currMetrics[i0] = d0 ? m2 : m0;
currMetrics[i1] = d1 ? m3 : m1;
calculate_path_metric(cost0, cost1, hist, j);
}
std::swap(currMetrics, prevMetrics);
hindex += 1;