{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3ac85f42",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "导入完成\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy import signal\n",
    "from scipy.fft import fft, fftfreq\n",
    "from scipy.optimize import curve_fit\n",
    "\n",
    "# 设置中文字体\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "\n",
    "print(\"导入完成\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17226faa",
   "metadata": {},
   "source": [
    "## 1. 定义转换参数和函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9ecc3561",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "转换参数和函数定义完成\n",
      "ADC分辨率: 0.298 uV\n",
      "最大传感器输出: 10.00 mV\n"
     ]
    }
   ],
   "source": [
    "# ADC和传感器参数\n",
    "ADC_BITS = 24\n",
    "ADC_FULL_SCALE = 2.5  # V\n",
    "AMPLIFIER_GAIN = 320\n",
    "EXCITATION_VOLTAGE = 5  # V\n",
    "SENSITIVITY = 2e-3  # 2mV/V\n",
    "\n",
    "# 满量程力值（N）\n",
    "FULL_SCALE_FORCE = [20, 20, 5]  # 通道1,2,3的满量程\n",
    "\n",
    "# ADC分辨率\n",
    "ADC_RESOLUTION = 2 * ADC_FULL_SCALE / (2**ADC_BITS)\n",
    "\n",
    "def adc_to_voltage(adc_value):\n",
    "    \"\"\"ADC值转换为电压（放大器输出）\"\"\"\n",
    "    return adc_value * ADC_RESOLUTION\n",
    "\n",
    "def voltage_to_sensor_output(voltage):\n",
    "    \"\"\"放大器输出电压转换为传感器输出电压\"\"\"\n",
    "    return voltage / AMPLIFIER_GAIN\n",
    "\n",
    "def sensor_output_to_force(sensor_voltage, full_scale_force):\n",
    "    \"\"\"传感器输出电压转换为力值\"\"\"\n",
    "    # 传感器输出电压范围：SENSITIVITY * EXCITATION_VOLTAGE (在满量程时)\n",
    "    max_sensor_output = SENSITIVITY * EXCITATION_VOLTAGE\n",
    "    return (sensor_voltage / max_sensor_output) * full_scale_force\n",
    "\n",
    "def adc_to_force(adc_value, channel_index):\n",
    "    \"\"\"ADC值直接转换为力值\"\"\"\n",
    "    voltage = adc_to_voltage(adc_value)\n",
    "    sensor_voltage = voltage_to_sensor_output(voltage)\n",
    "    force = sensor_output_to_force(sensor_voltage, FULL_SCALE_FORCE[channel_index])\n",
    "    return force\n",
    "\n",
    "print(\"转换参数和函数定义完成\")\n",
    "print(f\"ADC分辨率: {ADC_RESOLUTION*1e6:.3f} uV\")\n",
    "print(f\"最大传感器输出: {SENSITIVITY * EXCITATION_VOLTAGE * 1000:.2f} mV\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22a31836",
   "metadata": {},
   "source": [
    "## 2. 分析静止数据，拟合温飘曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5544acf0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "静止数据形状: (100, 4)\n",
      "列名: ['time_s', 'ch0', 'ch1', 'ch2']\n",
      "\n",
      "前5行:\n",
      "     time_s        ch0       ch1       ch2\n",
      "0  0.000000 -1743786.1 -945281.6 -588089.1\n",
      "1  0.223837 -1743918.5 -944662.3 -588171.1\n",
      "2  0.447674 -1744119.1 -944831.7 -588030.9\n",
      "3  0.671512 -1743570.3 -944659.9 -588060.1\n",
      "4  0.895349 -1744010.5 -944799.7 -587867.3\n"
     ]
    }
   ],
   "source": [
    "# 读取静止数据\n",
    "df_still = pd.read_csv('stealth_no_ctrl_still.csv')\n",
    "print(f\"静止数据形状: {df_still.shape}\")\n",
    "print(f\"列名: {df_still.columns.tolist()}\")\n",
    "print(f\"\\n前5行:\")\n",
    "print(df_still.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5f5db902",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 26102 (\\N{CJK UNIFIED IDEOGRAPH-65F6}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 38388 (\\N{CJK UNIFIED IDEOGRAPH-95F4}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 20540 (\\N{CJK UNIFIED IDEOGRAPH-503C}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 36890 (\\N{CJK UNIFIED IDEOGRAPH-901A}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 36947 (\\N{CJK UNIFIED IDEOGRAPH-9053}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 28201 (\\N{CJK UNIFIED IDEOGRAPH-6E29}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 39128 (\\N{CJK UNIFIED IDEOGRAPH-98D8}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 25311 (\\N{CJK UNIFIED IDEOGRAPH-62DF}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 21512 (\\N{CJK UNIFIED IDEOGRAPH-5408}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 26012 (\\N{CJK UNIFIED IDEOGRAPH-659C}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 29575 (\\N{CJK UNIFIED IDEOGRAPH-7387}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 25130 (\\N{CJK UNIFIED IDEOGRAPH-622A}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 36317 (\\N{CJK UNIFIED IDEOGRAPH-8DDD}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 21407 (\\N{CJK UNIFIED IDEOGRAPH-539F}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 22987 (\\N{CJK UNIFIED IDEOGRAPH-59CB}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 25968 (\\N{CJK UNIFIED IDEOGRAPH-6570}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/tmp/ipykernel_3067029/318049578.py:27: UserWarning: Glyph 25454 (\\N{CJK UNIFIED IDEOGRAPH-636E}) missing from current font.\n",
      "  plt.tight_layout()\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 20540 (\\N{CJK UNIFIED IDEOGRAPH-503C}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 36890 (\\N{CJK UNIFIED IDEOGRAPH-901A}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 36947 (\\N{CJK UNIFIED IDEOGRAPH-9053}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 28201 (\\N{CJK UNIFIED IDEOGRAPH-6E29}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 39128 (\\N{CJK UNIFIED IDEOGRAPH-98D8}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 25311 (\\N{CJK UNIFIED IDEOGRAPH-62DF}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 21512 (\\N{CJK UNIFIED IDEOGRAPH-5408}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 26012 (\\N{CJK UNIFIED IDEOGRAPH-659C}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 29575 (\\N{CJK UNIFIED IDEOGRAPH-7387}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 25130 (\\N{CJK UNIFIED IDEOGRAPH-622A}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 36317 (\\N{CJK UNIFIED IDEOGRAPH-8DDD}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 26102 (\\N{CJK UNIFIED IDEOGRAPH-65F6}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 38388 (\\N{CJK UNIFIED IDEOGRAPH-95F4}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 21407 (\\N{CJK UNIFIED IDEOGRAPH-539F}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 22987 (\\N{CJK UNIFIED IDEOGRAPH-59CB}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 25968 (\\N{CJK UNIFIED IDEOGRAPH-6570}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n",
      "/home/frank14f/anaconda3/envs/pycuda_3_10/lib/python3.10/site-packages/IPython/core/pylabtools.py:152: UserWarning: Glyph 25454 (\\N{CJK UNIFIED IDEOGRAPH-636E}) missing from current font.\n",
      "  fig.canvas.print_figure(bytes_io, **kw)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 185724 characters]",
      "text/plain": [
       "<Figure size 1200x1000 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "温飘参数 (斜率, 截距):\n",
      "通道1: 斜率=1.925925e+02 ADC/s, 截距=-1743699.86 ADC\n",
      "通道2: 斜率=1.999936e+02 ADC/s, 截距=-944717.74 ADC\n",
      "通道3: 斜率=1.416812e+02 ADC/s, 截距=-588114.22 ADC\n"
     ]
    }
   ],
   "source": [
    "# 提取时间和ADC数据\n",
    "time_still = df_still.iloc[:, 0].values\n",
    "adc_still = df_still.iloc[:, 1:4].values  # 3个通道\n",
    "\n",
    "# 线性拟合函数\n",
    "def linear_func(x, a, b):\n",
    "    return a * x + b\n",
    "\n",
    "# 对每个通道拟合温飘曲线\n",
    "drift_params = []  # 存储每个通道的(斜率, 截距)\n",
    "\n",
    "fig, axes = plt.subplots(3, 1, figsize=(12, 10))\n",
    "for i in range(3):\n",
    "    # 拟合线性温飘\n",
    "    params, _ = curve_fit(linear_func, time_still, adc_still[:, i])\n",
    "    drift_params.append(params)\n",
    "    \n",
    "    # 绘制原始数据和拟合曲线\n",
    "    axes[i].plot(time_still, adc_still[:, i], 'b-', alpha=0.5, label='Raw Data')\n",
    "    axes[i].plot(time_still, linear_func(time_still, *params), 'r-', linewidth=2, label='Drift Fit')\n",
    "    axes[i].set_xlabel('Time (s)')\n",
    "    axes[i].set_ylabel(f'ADC Value Ch{i+1}')\n",
    "    axes[i].legend()\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "    axes[i].set_title(f'Channel {i+1} Drift Fitting (slope={params[0]:.2e}, offset={params[1]:.2f})')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 保存温飘参数\n",
    "drift_params = np.array(drift_params)\n",
    "print(\"\\n温飘参数 (斜率, 截距):\")\n",
    "for i, params in enumerate(drift_params):\n",
    "    print(f\"通道{i+1}: 斜率={params[0]:.6e} ADC/s, 截距={params[1]:.2f} ADC\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a99d57a2",
   "metadata": {},
   "source": [
    "## 3. 处理实验数据：去除温飘并转换为力值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "b440a07c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "实验数据形状: (500, 4)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 422020 characters]",
      "text/plain": [
       "<Figure size 1500x1000 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "力值统计:\n",
      "通道1: 均值=0.0101N, 标准差=0.0384N, 范围=[-0.1293, 0.1459]N\n",
      "通道2: 均值=0.0069N, 标准差=0.0432N, 范围=[-0.1989, 0.1481]N\n",
      "通道3: 均值=-0.0009N, 标准差=0.0065N, 范围=[-0.0249, 0.0258]N\n"
     ]
    }
   ],
   "source": [
    "# 读取实验数据\n",
    "df_exp = pd.read_csv('stealth_no_ctrl_towing_s0133_500sp.csv')\n",
    "print(f\"实验数据形状: {df_exp.shape}\")\n",
    "\n",
    "# 提取时间和ADC数据\n",
    "time_exp = df_exp.iloc[:, 0].values\n",
    "adc_exp = df_exp.iloc[:, 1:4].values\n",
    "\n",
    "# 去除温飘\n",
    "adc_corrected = np.zeros_like(adc_exp)\n",
    "for i in range(3):\n",
    "    # 计算该时刻的温飘值\n",
    "    drift = linear_func(time_exp, drift_params[i, 0], drift_params[i, 1])\n",
    "    # 去除温飘（减去拟合的漂移曲线）\n",
    "    adc_corrected[:, i] = adc_exp[:, i] - drift\n",
    "\n",
    "# 转换为力值\n",
    "force_data = np.zeros_like(adc_corrected)\n",
    "for i in range(3):\n",
    "    force_data[:, i] = adc_to_force(adc_corrected[:, i], i)\n",
    "\n",
    "# 绘制对比图\n",
    "fig, axes = plt.subplots(3, 2, figsize=(15, 10))\n",
    "for i in range(3):\n",
    "    # 左列：去漂前后ADC对比\n",
    "    axes[i, 0].plot(time_exp, adc_exp[:, i], 'b-', alpha=0.5, label='Raw ADC')\n",
    "    axes[i, 0].plot(time_exp, adc_corrected[:, i], 'r-', alpha=0.7, label='Corrected ADC')\n",
    "    axes[i, 0].set_xlabel('Time (s)')\n",
    "    axes[i, 0].set_ylabel('ADC Value')\n",
    "    axes[i, 0].legend()\n",
    "    axes[i, 0].grid(True, alpha=0.3)\n",
    "    axes[i, 0].set_title(f'Channel {i+1} ADC Comparison')\n",
    "    \n",
    "    # 右列：力值\n",
    "    axes[i, 1].plot(time_exp, force_data[:, i], 'g-', linewidth=1)\n",
    "    axes[i, 1].set_xlabel('Time (s)')\n",
    "    axes[i, 1].set_ylabel('Force (N)')\n",
    "    axes[i, 1].grid(True, alpha=0.3)\n",
    "    axes[i, 1].set_title(f'Channel {i+1} Force (Full Scale {FULL_SCALE_FORCE[i]}N)')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"\\n力值统计:\")\n",
    "for i in range(3):\n",
    "    print(f\"通道{i+1}: 均值={np.mean(force_data[:, i]):.4f}N, \"\n",
    "          f\"标准差={np.std(force_data[:, i]):.4f}N, \"\n",
    "          f\"范围=[{np.min(force_data[:, i]):.4f}, {np.max(force_data[:, i]):.4f}]N\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33926690",
   "metadata": {},
   "source": [
    "## 4. 傅立叶变换分析周期性信号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "10228937",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "采样率: 4.98 Hz\n",
      "采样间隔: 0.2008 s\n",
      "样本数: 500\n",
      "总时长: 100.21 s\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 188808 characters]",
      "text/plain": [
       "<Figure size 1400x1000 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "各通道前10个频率峰值:\n",
      "\n",
      "通道1:\n",
      "  1. 频率=1.2051 Hz, 振幅=0.013028 N\n",
      "  2. 频率=0.0199 Hz, 振幅=0.006833 N\n",
      "  3. 频率=1.3146 Hz, 振幅=0.004428 N\n",
      "  4. 频率=0.6374 Hz, 振幅=0.004412 N\n",
      "  5. 频率=1.2648 Hz, 振幅=0.004241 N\n",
      "  6. 频率=1.4640 Hz, 振幅=0.003469 N\n",
      "  7. 频率=1.0856 Hz, 振幅=0.003206 N\n",
      "  8. 频率=0.5478 Hz, 振幅=0.002825 N\n",
      "  9. 频率=1.0159 Hz, 振幅=0.002630 N\n",
      "  10. 频率=0.7071 Hz, 振幅=0.002584 N\n",
      "\n",
      "通道2:\n",
      "  1. 频率=1.2051 Hz, 振幅=0.015931 N\n",
      "  2. 频率=1.3146 Hz, 振幅=0.005709 N\n",
      "  3. 频率=0.0199 Hz, 振幅=0.005270 N\n",
      "  4. 频率=1.2648 Hz, 振幅=0.005074 N\n",
      "  5. 频率=0.6374 Hz, 振幅=0.004388 N\n",
      "  6. 频率=1.0856 Hz, 振幅=0.003812 N\n",
      "  7. 频率=0.7071 Hz, 振幅=0.003100 N\n",
      "  8. 频率=1.4640 Hz, 振幅=0.002888 N\n",
      "  9. 频率=0.9163 Hz, 振幅=0.002751 N\n",
      "  10. 频率=0.5478 Hz, 振幅=0.002701 N\n",
      "\n",
      "通道3:\n",
      "  1. 频率=0.6374 Hz, 振幅=0.000981 N\n",
      "  2. 频率=1.2051 Hz, 振幅=0.000969 N\n",
      "  3. 频率=0.2091 Hz, 振幅=0.000742 N\n",
      "  4. 频率=0.7569 Hz, 振幅=0.000686 N\n",
      "  5. 频率=0.4183 Hz, 振幅=0.000549 N\n",
      "  6. 频率=0.8565 Hz, 振幅=0.000549 N\n",
      "  7. 频率=0.3187 Hz, 振幅=0.000546 N\n",
      "  8. 频率=0.5577 Hz, 振幅=0.000512 N\n",
      "  9. 频率=0.3685 Hz, 振幅=0.000472 N\n",
      "  10. 频率=0.4681 Hz, 振幅=0.000471 N\n"
     ]
    }
   ],
   "source": [
    "# 计算采样率\n",
    "dt = np.mean(np.diff(time_exp))\n",
    "fs = 1 / dt  # 采样率\n",
    "N = len(time_exp)  # 样本数\n",
    "\n",
    "print(f\"采样率: {fs:.2f} Hz\")\n",
    "print(f\"采样间隔: {dt:.4f} s\")\n",
    "print(f\"样本数: {N}\")\n",
    "print(f\"总时长: {time_exp[-1] - time_exp[0]:.2f} s\")\n",
    "\n",
    "# 对每个通道进行FFT分析\n",
    "fig, axes = plt.subplots(3, 1, figsize=(14, 10))\n",
    "\n",
    "# 存储每个通道的频率峰值\n",
    "peak_frequencies = []\n",
    "\n",
    "for i in range(3):\n",
    "    # 去均值\n",
    "    signal_centered = force_data[:, i] - np.mean(force_data[:, i])\n",
    "    \n",
    "    # FFT\n",
    "    fft_values = fft(signal_centered)\n",
    "    fft_freq = fftfreq(N, dt)\n",
    "    \n",
    "    # 只取正频率部分\n",
    "    positive_freq_idx = fft_freq > 0\n",
    "    frequencies = fft_freq[positive_freq_idx]\n",
    "    amplitudes = np.abs(fft_values[positive_freq_idx]) / N  # 归一化振幅\n",
    "    \n",
    "    # 找出前10个峰值\n",
    "    # 使用scipy的find_peaks找峰值\n",
    "    peaks, properties = signal.find_peaks(amplitudes, height=np.max(amplitudes)*0.01, distance=5)\n",
    "    \n",
    "    # 按振幅排序，取前10个\n",
    "    peak_amplitudes = amplitudes[peaks]\n",
    "    sorted_indices = np.argsort(peak_amplitudes)[::-1]  # 降序\n",
    "    top_10_indices = sorted_indices[:min(10, len(sorted_indices))]\n",
    "    top_10_peaks = peaks[top_10_indices]\n",
    "    \n",
    "    # 存储峰值频率和振幅\n",
    "    peak_freqs = frequencies[top_10_peaks]\n",
    "    peak_amps = amplitudes[top_10_peaks]\n",
    "    peak_frequencies.append(list(zip(peak_freqs, peak_amps)))\n",
    "    \n",
    "    # 绘制频谱\n",
    "    axes[i].plot(frequencies, amplitudes, 'b-', linewidth=0.5, alpha=0.7)\n",
    "    axes[i].plot(frequencies[top_10_peaks], amplitudes[top_10_peaks], 'ro', markersize=8, label='Top 10 Peaks')\n",
    "    axes[i].set_xlabel('Frequency (Hz)')\n",
    "    axes[i].set_ylabel('Amplitude (N)')\n",
    "    axes[i].set_title(f'Channel {i+1} Frequency Spectrum (Full Scale {FULL_SCALE_FORCE[i]}N)')\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "    axes[i].legend()\n",
    "    axes[i].set_xlim([0, min(50, fs/2)])  # 显示0-50Hz或奈奎斯特频率\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 打印峰值频率\n",
    "print(\"\\n各通道前10个频率峰值:\")\n",
    "for i in range(3):\n",
    "    print(f\"\\n通道{i+1}:\")\n",
    "    for j, (freq, amp) in enumerate(peak_frequencies[i]):\n",
    "        print(f\"  {j+1}. 频率={freq:.4f} Hz, 振幅={amp:.6f} N\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9fd9f6b",
   "metadata": {},
   "source": [
    "## 5. 用主要频率成分重构信号（高采样率）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "bf5433f5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始采样率: 5.23 Hz\n",
      "重构采样率: 52.26 Hz\n",
      "重构样本数: 9990\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 432100 characters]",
      "text/plain": [
       "<Figure size 1600x1000 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "重构信号质量评估:\n",
      "通道1: RMSE=0.016664N, 最大误差=0.049624N, 相对RMSE=93.16%\n",
      "通道2: RMSE=0.013692N, 最大误差=0.038177N, 相对RMSE=89.44%\n",
      "通道3: RMSE=0.003011N, 最大误差=0.010959N, 相对RMSE=84.82%\n"
     ]
    }
   ],
   "source": [
    "# 设置更高的重构采样率（例如原来的10倍）\n",
    "fs_recon = fs * 10\n",
    "dt_recon = 1 / fs_recon\n",
    "time_recon = np.arange(time_exp[0], time_exp[-1], dt_recon)\n",
    "\n",
    "print(f\"原始采样率: {fs:.2f} Hz\")\n",
    "print(f\"重构采样率: {fs_recon:.2f} Hz\")\n",
    "print(f\"重构样本数: {len(time_recon)}\")\n",
    "\n",
    "# 对每个通道重构信号\n",
    "reconstructed_signals = []\n",
    "\n",
    "for i in range(3):\n",
    "    # 获取该通道的原始信号（去均值）\n",
    "    signal_centered = force_data[:, i] - np.mean(force_data[:, i])\n",
    "    signal_mean = np.mean(force_data[:, i])\n",
    "    \n",
    "    # FFT获取所有频率成分\n",
    "    fft_values = fft(signal_centered)\n",
    "    fft_freq = fftfreq(N, dt)\n",
    "    \n",
    "    # 重构信号：叠加前10个主要频率成分\n",
    "    reconstructed = np.zeros(len(time_recon))\n",
    "    \n",
    "    for freq, amp in peak_frequencies[i]:\n",
    "        # 找到对应频率的FFT系数\n",
    "        freq_idx = np.argmin(np.abs(fft_freq - freq))\n",
    "        \n",
    "        # 获取该频率的复数系数（包含振幅和相位）\n",
    "        complex_coeff = fft_values[freq_idx] / N\n",
    "        amplitude = np.abs(complex_coeff) * 2  # 单边谱需要乘2\n",
    "        phase = np.angle(complex_coeff)\n",
    "        \n",
    "        # 叠加该频率成分\n",
    "        reconstructed += amplitude * np.cos(2 * np.pi * freq * time_recon + phase)\n",
    "    \n",
    "    # 加回均值\n",
    "    reconstructed += signal_mean\n",
    "    reconstructed_signals.append(reconstructed)\n",
    "\n",
    "# 绘制原始信号和重构信号对比\n",
    "fig, axes = plt.subplots(3, 1, figsize=(16, 10))\n",
    "\n",
    "for i in range(3):\n",
    "    # 原始信号\n",
    "    axes[i].plot(time_exp, force_data[:, i], 'b-', linewidth=1, alpha=0.7, label='Original Signal')\n",
    "    # 重构信号\n",
    "    axes[i].plot(time_recon, reconstructed_signals[i], 'r-', linewidth=1.5, alpha=0.8, label='Reconstructed (Top 10 Peaks)')\n",
    "    axes[i].set_xlabel('Time (s)')\n",
    "    axes[i].set_ylabel('Force (N)')\n",
    "    axes[i].set_title(f'Channel {i+1} Signal Reconstruction (Full Scale {FULL_SCALE_FORCE[i]}N)')\n",
    "    axes[i].legend()\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 计算重构误差（使用插值到相同时间点）\n",
    "from scipy.interpolate import interp1d\n",
    "\n",
    "print(\"\\n重构信号质量评估:\")\n",
    "for i in range(3):\n",
    "    # 将重构信号插值到原始采样点\n",
    "    interp_func = interp1d(time_recon, reconstructed_signals[i], kind='linear', \n",
    "                           bounds_error=False, fill_value='extrapolate')\n",
    "    recon_at_orig_time = interp_func(time_exp)\n",
    "    \n",
    "    # 计算误差\n",
    "    error = force_data[:, i] - recon_at_orig_time\n",
    "    rmse = np.sqrt(np.mean(error**2))\n",
    "    max_error = np.max(np.abs(error))\n",
    "    relative_rmse = rmse / np.std(force_data[:, i]) * 100\n",
    "    \n",
    "    print(f\"通道{i+1}: RMSE={rmse:.6f}N, 最大误差={max_error:.6f}N, 相对RMSE={relative_rmse:.2f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd76c3dc",
   "metadata": {},
   "source": [
    "## 6. 带通滤波重构信号（0.1-0.5 Hz）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "60ca3cca",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义带通滤波器参数\n",
    "lowcut = 0.1  # Hz\n",
    "highcut = 0.5  # Hz\n",
    "\n",
    "print(f\"带通滤波器范围: {lowcut} - {highcut} Hz\")\n",
    "\n",
    "# 使用FFT方法进行带通滤波和重构\n",
    "filtered_signals = []\n",
    "\n",
    "for i in range(3):\n",
    "    # 获取该通道的原始信号（去均值）\n",
    "    signal_centered = force_data[:, i] - np.mean(force_data[:, i])\n",
    "    signal_mean = np.mean(force_data[:, i])\n",
    "    \n",
    "    # FFT\n",
    "    fft_values = fft(signal_centered)\n",
    "    fft_freq = fftfreq(N, dt)\n",
    "    \n",
    "    # 创建带通滤波器掩码\n",
    "    # 保留 lowcut <= |freq| <= highcut 的频率成分\n",
    "    filter_mask = (np.abs(fft_freq) >= lowcut) & (np.abs(fft_freq) <= highcut)\n",
    "    \n",
    "    # 应用滤波器\n",
    "    fft_filtered = fft_values.copy()\n",
    "    fft_filtered[~filter_mask] = 0  # 将滤波器外的频率成分置零\n",
    "    \n",
    "    # 逆FFT得到滤波后的时域信号\n",
    "    filtered_signal = np.fft.ifft(fft_filtered).real\n",
    "    \n",
    "    # 加回均值\n",
    "    filtered_signal += signal_mean\n",
    "    \n",
    "    filtered_signals.append(filtered_signal)\n",
    "\n",
    "print(\"带通滤波完成\")\n",
    "\n",
    "# 在高采样率下重构滤波信号\n",
    "filtered_signals_highres = []\n",
    "\n",
    "for i in range(3):\n",
    "    # 获取该通道的原始信号（去均值）\n",
    "    signal_centered = force_data[:, i] - np.mean(force_data[:, i])\n",
    "    signal_mean = np.mean(force_data[:, i])\n",
    "    \n",
    "    # FFT\n",
    "    fft_values = fft(signal_centered)\n",
    "    fft_freq = fftfreq(N, dt)\n",
    "    \n",
    "    # 重构信号：只使用0.1-0.5Hz的频率成分\n",
    "    reconstructed = np.zeros(len(time_recon))\n",
    "    \n",
    "    # 遍历所有在滤波范围内的频率\n",
    "    for freq_idx, freq in enumerate(fft_freq):\n",
    "        if lowcut <= abs(freq) <= highcut:\n",
    "            # 获取该频率的复数系数\n",
    "            complex_coeff = fft_values[freq_idx] / N\n",
    "            amplitude = np.abs(complex_coeff) * 2  # 单边谱需要乘2\n",
    "            phase = np.angle(complex_coeff)\n",
    "            \n",
    "            # 叠加该频率成分\n",
    "            reconstructed += amplitude * np.cos(2 * np.pi * freq * time_recon + phase)\n",
    "    \n",
    "    # 加回均值\n",
    "    reconstructed += signal_mean\n",
    "    filtered_signals_highres.append(reconstructed)\n",
    "\n",
    "print(f\"高采样率重构完成 ({fs_recon:.2f} Hz)\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91376605",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 绘制带通滤波结果对比\n",
    "fig, axes = plt.subplots(3, 1, figsize=(16, 10))\n",
    "\n",
    "for i in range(3):\n",
    "    # 原始信号\n",
    "    axes[i].plot(time_exp, force_data[:, i], 'b-', linewidth=1, alpha=0.5, label='Original Signal')\n",
    "    # 带通滤波信号（原始采样率）\n",
    "    axes[i].plot(time_exp, filtered_signals[i], 'g-', linewidth=1.5, alpha=0.8, label=f'Bandpass Filtered ({lowcut}-{highcut} Hz)')\n",
    "    # 带通滤波信号（高采样率）\n",
    "    axes[i].plot(time_recon, filtered_signals_highres[i], 'r-', linewidth=1, alpha=0.7, label=f'High-res Filtered ({fs_recon:.0f} Hz)')\n",
    "    \n",
    "    axes[i].set_xlabel('Time (s)')\n",
    "    axes[i].set_ylabel('Force (N)')\n",
    "    axes[i].set_title(f'Channel {i+1} Bandpass Filter Reconstruction (Full Scale {FULL_SCALE_FORCE[i]}N)')\n",
    "    axes[i].legend()\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 计算滤波信号的统计信息\n",
    "print(\"\\n带通滤波信号统计:\")\n",
    "for i in range(3):\n",
    "    print(f\"通道{i+1}:\")\n",
    "    print(f\"  原始信号: 均值={np.mean(force_data[:, i]):.4f}N, 标准差={np.std(force_data[:, i]):.4f}N\")\n",
    "    print(f\"  滤波信号: 均值={np.mean(filtered_signals[i]):.4f}N, 标准差={np.std(filtered_signals[i]):.4f}N\")\n",
    "    print(f\"  能量保留率: {(np.std(filtered_signals[i])/np.std(force_data[:, i])*100):.2f}%\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54660fc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 显示带通滤波前后的频谱对比\n",
    "fig, axes = plt.subplots(3, 1, figsize=(14, 10))\n",
    "\n",
    "for i in range(3):\n",
    "    # 原始信号的频谱\n",
    "    signal_centered = force_data[:, i] - np.mean(force_data[:, i])\n",
    "    fft_orig = fft(signal_centered)\n",
    "    fft_freq_orig = fftfreq(N, dt)\n",
    "    positive_idx = fft_freq_orig > 0\n",
    "    freq_orig = fft_freq_orig[positive_idx]\n",
    "    amp_orig = np.abs(fft_orig[positive_idx]) / N\n",
    "    \n",
    "    # 滤波信号的频谱\n",
    "    filtered_centered = filtered_signals[i] - np.mean(filtered_signals[i])\n",
    "    fft_filt = fft(filtered_centered)\n",
    "    amp_filt = np.abs(fft_filt[positive_idx]) / N\n",
    "    \n",
    "    # 绘制频谱对比\n",
    "    axes[i].plot(freq_orig, amp_orig, 'b-', linewidth=0.5, alpha=0.7, label='Original Spectrum')\n",
    "    axes[i].plot(freq_orig, amp_filt, 'r-', linewidth=1.5, alpha=0.8, label='Filtered Spectrum')\n",
    "    \n",
    "    # 标记滤波范围\n",
    "    axes[i].axvline(lowcut, color='g', linestyle='--', linewidth=2, alpha=0.7, label=f'Filter Range')\n",
    "    axes[i].axvline(highcut, color='g', linestyle='--', linewidth=2, alpha=0.7)\n",
    "    axes[i].axvspan(lowcut, highcut, alpha=0.1, color='green')\n",
    "    \n",
    "    axes[i].set_xlabel('Frequency (Hz)')\n",
    "    axes[i].set_ylabel('Amplitude (N)')\n",
    "    axes[i].set_title(f'Channel {i+1} Spectrum Comparison')\n",
    "    axes[i].legend()\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "    axes[i].set_xlim([0, min(2, fs/2)])  # 显示0-2Hz\n",
    "    axes[i].set_ylim(bottom=0)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "839fe552",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 局部放大显示带通滤波效果\n",
    "window_start_bp = time_exp[len(time_exp)//3]\n",
    "window_duration_bp = 5  # 显示5秒\n",
    "window_end_bp = window_start_bp + window_duration_bp\n",
    "\n",
    "fig, axes = plt.subplots(3, 1, figsize=(16, 10))\n",
    "\n",
    "for i in range(3):\n",
    "    # 找到窗口内的数据\n",
    "    orig_mask = (time_exp >= window_start_bp) & (time_exp <= window_end_bp)\n",
    "    recon_mask = (time_recon >= window_start_bp) & (time_recon <= window_end_bp)\n",
    "    \n",
    "    # 原始信号\n",
    "    axes[i].plot(time_exp[orig_mask], force_data[orig_mask, i], 'b.-', \n",
    "                linewidth=1, markersize=3, alpha=0.5, label='Original Signal')\n",
    "    # 带通滤波信号（高采样率）\n",
    "    axes[i].plot(time_recon[recon_mask], filtered_signals_highres[i][recon_mask], 'r-', \n",
    "                linewidth=2, alpha=0.9, label=f'Bandpass {lowcut}-{highcut} Hz')\n",
    "    \n",
    "    axes[i].set_xlabel('Time (s)')\n",
    "    axes[i].set_ylabel('Force (N)')\n",
    "    axes[i].set_title(f'Channel {i+1} Zoomed View - Bandpass Filter ({window_start_bp:.1f}s - {window_end_bp:.1f}s)')\n",
    "    axes[i].legend()\n",
    "    axes[i].grid(True, alpha=0.3)\n",
    "    axes[i].set_xlim([window_start_bp, window_end_bp])\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1defaba",
   "metadata": {},
   "source": [
    "## 7. 保存处理结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22bbf4fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 保存处理后的力值数据\n",
    "df_force = pd.DataFrame({\n",
    "    'time': time_exp,\n",
    "    'force_ch1': force_data[:, 0],\n",
    "    'force_ch2': force_data[:, 1],\n",
    "    'force_ch3': force_data[:, 2]\n",
    "})\n",
    "df_force.to_csv('stealth_processed_force.csv', index=False)\n",
    "print(\"已保存处理后的力值数据到: stealth_processed_force.csv\")\n",
    "\n",
    "# 保存带通滤波后的信号（原始采样率）\n",
    "df_filtered = pd.DataFrame({\n",
    "    'time': time_exp,\n",
    "    'force_ch1_filtered': filtered_signals[0],\n",
    "    'force_ch2_filtered': filtered_signals[1],\n",
    "    'force_ch3_filtered': filtered_signals[2]\n",
    "})\n",
    "df_filtered.to_csv('stealth_bandpass_filtered.csv', index=False)\n",
    "print(f\"已保存带通滤波信号到: stealth_bandpass_filtered.csv\")\n",
    "\n",
    "# 保存带通滤波后的高采样率信号\n",
    "df_filtered_highres = pd.DataFrame({\n",
    "    'time': time_recon,\n",
    "    'force_ch1_filtered': filtered_signals_highres[0],\n",
    "    'force_ch2_filtered': filtered_signals_highres[1],\n",
    "    'force_ch3_filtered': filtered_signals_highres[2]\n",
    "})\n",
    "df_filtered_highres.to_csv('stealth_bandpass_filtered_highres.csv', index=False)\n",
    "print(f\"已保存高采样率带通滤波信号到: stealth_bandpass_filtered_highres.csv\")\n",
    "\n",
    "# 保存峰值重构的高采样率信号\n",
    "df_recon = pd.DataFrame({\n",
    "    'time': time_recon,\n",
    "    'force_ch1_recon': reconstructed_signals[0],\n",
    "    'force_ch2_recon': reconstructed_signals[1],\n",
    "    'force_ch3_recon': reconstructed_signals[2]\n",
    "})\n",
    "df_recon.to_csv('stealth_peak_reconstructed.csv', index=False)\n",
    "print(\"已保存峰值重构信号数据到: stealth_peak_reconstructed.csv\")\n",
    "\n",
    "# 保存频率峰值信息\n",
    "with open('frequency_peaks.txt', 'w', encoding='utf-8') as f:\n",
    "    f.write(\"各通道主要频率成分\\n\")\n",
    "    f.write(\"=\"*60 + \"\\n\\n\")\n",
    "    for i in range(3):\n",
    "        f.write(f\"通道{i+1} (满量程{FULL_SCALE_FORCE[i]}N):\\n\")\n",
    "        for j, (freq, amp) in enumerate(peak_frequencies[i]):\n",
    "            f.write(f\"  {j+1}. 频率={freq:.4f} Hz, 振幅={amp:.6f} N\\n\")\n",
    "        f.write(\"\\n\")\n",
    "print(\"已保存频率峰值信息到: frequency_peaks.txt\")\n",
    "\n",
    "# 保存温飘参数\n",
    "np.savetxt('drift_parameters.txt', drift_params, \n",
    "           header='通道1-3的温飘参数，每行为[斜率(ADC/s), 截距(ADC)]',\n",
    "           fmt='%.10e')\n",
    "print(\"已保存温飘参数到: drift_parameters.txt\")\n",
    "\n",
    "# 保存处理参数摘要\n",
    "with open('processing_summary.txt', 'w', encoding='utf-8') as f:\n",
    "    f.write(\"数据处理参数摘要\\n\")\n",
    "    f.write(\"=\"*60 + \"\\n\\n\")\n",
    "    f.write(f\"原始采样率: {fs:.2f} Hz\\n\")\n",
    "    f.write(f\"重构采样率: {fs_recon:.2f} Hz\\n\")\n",
    "    f.write(f\"样本数: {N}\\n\")\n",
    "    f.write(f\"总时长: {time_exp[-1] - time_exp[0]:.2f} s\\n\\n\")\n",
    "    f.write(f\"带通滤波范围: {lowcut} - {highcut} Hz\\n\\n\")\n",
    "    f.write(\"ADC参数:\\n\")\n",
    "    f.write(f\"  位数: {ADC_BITS} bit\\n\")\n",
    "    f.write(f\"  满量程: ±{ADC_FULL_SCALE} V\\n\")\n",
    "    f.write(f\"  放大器增益: {AMPLIFIER_GAIN}x\\n\")\n",
    "    f.write(f\"  传感器激励电压: {EXCITATION_VOLTAGE} V\\n\")\n",
    "    f.write(f\"  传感器灵敏度: {SENSITIVITY*1000} mV/V\\n\")\n",
    "print(\"已保存处理参数摘要到: processing_summary.txt\")\n",
    "\n",
    "print(\"\\n所有处理完成！\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pycuda_3_10",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
