TinyDSP · SIGNAL · CONVOLUTION — 实现与源码¶
本页保留完整源码摘录,按文件展开阅读。先看设计说明了解数据流、接口和算法,再核对实现;源码摘录可能属于历史版本,当前实现请以所选工程为准。
tiny_conv.h¶
展开 tiny_conv.h · 91 行
/**
* @file tiny_conv.h
* @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
* @brief tiny_conv | code | header
* @version 1.0
* @date 2025-04-27
* @copyright Copyright (c) 2025
*
*/
#pragma once
/* DEPENDENCIES */
// tiny_dsp configuration file
#include "tiny_dsp_config.h"
// ESP32 DSP Library for Acceleration
#if MCU_PLATFORM_SELECTED == MCU_PLATFORM_ESP32 // ESP32 DSP library
#include "dsps_conv.h"
#include "dspi_conv.h"
#endif
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @name: tiny_conv_f32
* @brief Convolution function (full mode, zero padding implicit)
*
* @param Signal The input signal array
* @param siglen The length of the input signal array (> 0)
* @param Kernel The input kernel array
* @param kernlen The length of the input kernel array (> 0)
* @param convout The output array for the convolution result.
* Caller MUST provide at least (siglen + kernlen - 1) elements.
*
* @note On ESP32 the underlying ESP-DSP routine additionally requires
* siglen >= kernlen; the generic fallback handles both orderings.
*
* @return tiny_error_t
*/
tiny_error_t tiny_conv_f32(const float *Signal, const int siglen, const float *Kernel, const int kernlen, float *convout);
typedef enum
{
TINY_PADDING_ZERO = 0, // Zero padding
TINY_PADDING_SYMMETRIC = 1, // Symmetric reflection
TINY_PADDING_PERIODIC = 2 // Periodic extension
} tiny_padding_mode_t;
typedef enum
{
TINY_CONV_FULL = 0, // Full convolution (len = siglen + kernlen - 1)
TINY_CONV_HEAD = 1, // Head mode (first lkern points)
TINY_CONV_CENTER = 2, // Centered mode (output siglen points)
TINY_CONV_TAIL = 3 // Tail mode (last lkern points)
} tiny_conv_mode_t;
/**
* @name: tiny_conv_ex_f32
* @brief Extended convolution function with padding and mode options
*
* @param Signal The input signal array
* @param siglen The length of the input signal array (> 0)
* @param Kernel The input kernel array
* @param kernlen The length of the input kernel array (> 0)
* @param convout The output buffer for the convolution result.
* Regardless of conv_mode, the caller MUST provide
* at least (siglen + kernlen - 1) elements: the routine
* first writes the full convolution into convout and then
* shifts the requested slice to the front in place.
* After return, the meaningful length is:
* - TINY_CONV_FULL : siglen + kernlen - 1
* - TINY_CONV_HEAD : kernlen
* - TINY_CONV_CENTER : siglen
* - TINY_CONV_TAIL : kernlen
* @param padding_mode Padding mode (zero, symmetric, periodic)
* @param conv_mode Convolution mode (full, head, center, tail)
*
* @return tiny_error_t
*/
tiny_error_t tiny_conv_ex_f32(const float *Signal, const int siglen, const float *Kernel, const int kernlen, float *convout, tiny_padding_mode_t padding_mode, tiny_conv_mode_t conv_mode);
#ifdef __cplusplus
}
#endif
tiny_conv.c¶
展开 tiny_conv.c · 227 行
/**
* @file tiny_conv.c
* @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
* @brief tiny_conv | code | source
* @version 1.0
* @date 2025-04-27
* @copyright Copyright (c) 2025
*
*/
/* DEPENDENCIES */
#include "tiny_conv.h"
/**
* @name: tiny_conv_f32
* @brief Convolution function
*
* @param Signal The input signal array
* @param siglen The length of the input signal array
* @param Kernel The input kernel array
* @param kernlen The length of the input kernel array
* @param convout The output array for the convolution result
*
* @return tiny_error_t
*/
tiny_error_t tiny_conv_f32(const float *Signal, const int siglen, const float *Kernel, const int kernlen, float *convout)
{
if (NULL == Signal || NULL == Kernel || NULL == convout)
{
return TINY_ERR_DSP_NULL_POINTER;
}
if (siglen <= 0 || kernlen <= 0)
{
return TINY_ERR_DSP_INVALID_PARAM;
}
#if MCU_PLATFORM_SELECTED == MCU_PLATFORM_ESP32
/* The ESP-DSP backend requires siglen >= kernlen. The generic fallback
* below handles both orderings via its three-stage form. */
if (siglen < kernlen)
{
return TINY_ERR_DSP_INVALID_PARAM;
}
dsps_conv_f32(Signal, siglen, Kernel, kernlen, convout);
#else
const float *sig = Signal;
const float *kern = Kernel;
int lsig = siglen;
int lkern = kernlen;
// stage I
for (int n = 0; n < lkern; n++)
{
size_t k;
convout[n] = 0;
for (k = 0; k <= n; k++)
{
convout[n] += sig[k] * kern[n - k];
}
}
// stage II
for (int n = lkern; n < lsig; n++)
{
size_t kmin, kmax, k;
convout[n] = 0;
kmin = n - lkern + 1;
kmax = n;
for (k = kmin; k <= kmax; k++)
{
convout[n] += sig[k] * kern[n - k];
}
}
// stage III
for (int n = lsig; n < lsig + lkern - 1; n++)
{
size_t kmin, kmax, k;
convout[n] = 0;
kmin = n - lkern + 1;
kmax = lsig - 1;
for (k = kmin; k <= kmax; k++)
{
convout[n] += sig[k] * kern[n - k];
}
}
#endif
return TINY_OK;
}
/**
* @name: tiny_conv_ex_f32
* @brief Extended convolution function with padding and mode options
*
* @param Signal The input signal array
* @param siglen The length of the input signal array
* @param Kernel The input kernel array
* @param kernlen The length of the input kernel array
* @param convout The output array for the convolution result
* @param padding_mode Padding mode (zero, symmetric, periodic)
* @param conv_mode Convolution mode (full, head, center, tail)
*
* @return tiny_error_t
*/
tiny_error_t tiny_conv_ex_f32(const float *Signal, const int siglen,
const float *Kernel, const int kernlen,
float *convout,
tiny_padding_mode_t padding_mode,
tiny_conv_mode_t conv_mode)
{
if (NULL == Signal || NULL == Kernel || NULL == convout)
{
return TINY_ERR_DSP_NULL_POINTER;
}
if (siglen <= 0 || kernlen <= 0)
{
return TINY_ERR_DSP_INVALID_PARAM;
}
#if MCU_PLATFORM_SELECTED == MCU_PLATFORM_ESP32
/* Fast path through ESP-DSP only when its precondition is met. */
if (padding_mode == TINY_PADDING_ZERO && conv_mode == TINY_CONV_FULL &&
siglen >= kernlen)
{
dsps_conv_f32(Signal, siglen, Kernel, kernlen, convout);
return TINY_OK;
}
#endif
int pad_len = kernlen - 1;
int padded_len = siglen + 2 * pad_len;
float *padded_signal = (float *)calloc(padded_len, sizeof(float));
if (padded_signal == NULL)
{
return TINY_ERR_DSP_MEMORY_ALLOC;
}
// Fill padded signal
switch (padding_mode)
{
case TINY_PADDING_ZERO:
// Middle copy only, left and right are zeros (calloc already zeroed)
memcpy(padded_signal + pad_len, Signal, sizeof(float) * siglen);
break;
case TINY_PADDING_SYMMETRIC:
for (int i = 0; i < pad_len; i++)
{
padded_signal[pad_len - 1 - i] = Signal[i]; // Mirror left
padded_signal[pad_len + siglen + i] = Signal[siglen - 1 - i]; // Mirror right
}
memcpy(padded_signal + pad_len, Signal, sizeof(float) * siglen); // Copy center
break;
case TINY_PADDING_PERIODIC:
for (int i = 0; i < pad_len; i++)
{
padded_signal[pad_len - 1 - i] = Signal[(siglen - pad_len + i) % siglen]; // Wrap left
padded_signal[pad_len + siglen + i] = Signal[i % siglen]; // Wrap right
}
memcpy(padded_signal + pad_len, Signal, sizeof(float) * siglen); // Copy center
break;
default:
free(padded_signal);
return TINY_ERR_DSP_INVALID_PARAM;
}
// Full convolution
int convlen_full = siglen + kernlen - 1;
for (int n = 0; n < convlen_full; n++)
{
float sum = 0.0f;
for (int k = 0; k < kernlen; k++)
{
sum += padded_signal[n + k] * Kernel[kernlen - 1 - k]; // Convolution is flip+slide
}
convout[n] = sum;
}
free(padded_signal);
// Handle output mode
if (conv_mode == TINY_CONV_FULL)
{
return TINY_OK;
}
else
{
int start_idx = 0;
int out_len = 0;
switch (conv_mode)
{
case TINY_CONV_HEAD:
start_idx = 0;
out_len = kernlen;
break;
case TINY_CONV_CENTER:
start_idx = (kernlen - 1) / 2;
out_len = siglen;
break;
case TINY_CONV_TAIL:
start_idx = siglen - 1;
out_len = kernlen;
break;
default:
return TINY_ERR_DSP_INVALID_MODE;
}
/* The selected slice may overlap with the head of convout, so use
* memmove rather than a forward copy / memcpy. */
if (start_idx > 0 && out_len > 0)
{
memmove(convout, convout + start_idx, sizeof(float) * (size_t)out_len);
}
}
return TINY_OK;
}