方法一: 积分移项
这个移项是不是90度, 不太好说. 得到的幅值会变大,需要缩小. 而且还有相位逐渐下移的趋势. 下移的原因未知.也未深入探索, 因计算量比较大, 也没采用顾抛弃掉.
private double[] 积分移相90度( List<float> sindata)
{
double[] cosdata = new double[sindata.Count];
double s = 0;
for (int i = 0; i < sindata.Count; i++)
{
var d = sindata[i];
s += sindata[i];
cosdata[i] =s;
}
return cosdata;
}
方法二: 索引移相位
利用数字计算机的下标索引, 将数值提前或者移后1/4
第一步,先测量出,两个正弦波峰值之间数据的个数 N.
第二步,将下标索引 前移1/4 或者后移 3/4.
/*
检测波峰测量周期宽度 代码比较简单,速度也比较快, 如果是程序生成的波形, 比较容易测量两个波峰的数值. 如果是ADC转换测量得到的数据. 测量峰值比较麻烦. . . 经过测试,此测量峰值求脉宽算法只对完美的正弦波比较适用. 对于有点干扰杂波的不太适用, 会陷入局部最小宽度. 不建议使用, 使用过零点检测脉宽更合适.
*/
private void 检测波峰测量周期宽度(List<float> sindata,out double width_count, out double avg_max_data)
{
//求索引的个数
double pre = double.MinValue;
double max = double.MinValue;
int maxIndex = 0;
//int preIndex = 0;
List<int> 波峰Index = new List<int>();
List<double> maxs = new List<double>();
//上升或下降次数
int upcount = 0;
int downcount = 0;
for (int i = 1; i < sindata.Count; i++)
{
if (sindata[i] > sindata[i-1] )
{
upcount++;
max = sindata[i];
maxIndex = i;
//连续10个以上的,上升视为真正的上升
if (upcount > 10)
{
downcount = 0;
}
}
//连续下降2次的视为真正的下降
else if ( sindata[i] < sindata[i - 1])
{
downcount ++;
//下降了10个数, 视为真正的下降,防止波形干扰.
if (downcount == 10 )
{
//说明开始走下降趋势,那么认为 max 中的值便是真的波峰值.
波峰Index.Add(maxIndex);
maxs.Add(max);
max = double.MinValue;
maxIndex = 0;
upcount = 0;
if (波峰Index.Count >= 10)
{
break;
}
}
}
}
var juli = new List<int>();
for (int i = 1; i < 波峰Index.Count; i++)
{
juli.Add(波峰Index[i] - 波峰Index[i - 1]);
}
width_count = juli.Average();
avg_max_data = maxs.Average();
return;
}
检测过零点测量周期宽度
/// 检测过零点测量周期宽度, 要求数据必须无直流分量.否则无效.
/// </summary>
/// <param name="sindata"></param>
private double 检测过零点测量周期宽度(List<float> sindata )
{
//求索引的个数
List<int> 过零点Index = new List<int>();
//上升或下降次数
for (int i = 1; i < sindata.Count; i++)
{
if (sindata[i -1] <= 0 && sindata[i]>=0)
{
过零点Index.Add(i);
if (过零点Index.Count >= 10)
{
break;
}
}
}
var juli = new List<int>();
for (int i = 1; i < 过零点Index.Count; i++)
{
juli.Add(过零点Index[i] - 过零点Index[i - 1]);
}
var width_count = juli.Average();
return width_count;
}
索引实现移相的使用方法.
//去掉参考信号的直流部分
//取前面10000个数据,求平均值,平均值约等于直流分量,
//完美的去直流方案,建议通过在电路中串联一个合适的电容.起到隔直通交的作用.
var sinAvg = Sin_data.Take(10000).Average();
for (int i = 0; i < LN; i++)
{
Sin_data[i] = Sin_data[i] - sinAvg;
}
//2.移相90度
//2.1 移项先计算两个波峰之间的数据的个数.
//2.2 知道了每个波峰之间数据的个数, 再把索引提前1/4 就是移项90度.
//var cos_data = 积分移相90度(Sin_data);
double width_count = 检测过零点测量周期宽度(Sin_data);
//360度往前移相位1/4是90度. 往后移3/4是270度也是90度,也是同一个相位. 为了程序更容易运行,减少一个判断. 无法取未来的数据, 可以改成取历史的数据
//所以从第270个相位的数据, 开始, 一般情况下. 数据很多的, 不在乎这点数据.
var his270index = (int) width_count / 4 * 3;
//var N90 = this.trackBar1.Value;
for (int i = his270index; i < LN; i++)
{
//this.chartMathNet.Series[0].Points.AddXY(i, GD_data[i]);
//this.chartMathNet.Series[1].Points.AddXY(i, A_data[i]);
this.chartMathNet.Series[2].Points.AddXY(i, Sin_data[i]); //显示
this.chartMathNet.Series[0].Points.AddXY(i, Sin_data[i - his270index]); //移相90度后的数据显示
}
经过测试, 更改索引实现移项的算法还是比较快速和稳定的.建议使用过零检测算法.