using System; namespace SDR { class Osc { double phase = 0; // 位相値 double PI = 3.141592653589793238; // パイの値 // // 任意の周波数の信号を作成する(負の周波数にも対応) // public void CreateSignal(double[] real, double[] imag, double freq, double level) { for (int i = 0; i < 1024; i++) { real[i] = Math.Cos(phase) * level; // X軸の座標を生成 imag[i] = Math.Sin(phase) * level; // Y軸の座標を生成 phase += freq / 8000 * PI; // 位相を進める if (phase > 2 * PI) // 一周したら phase -= 2 * PI; // 必ず0から2*PIの範囲に収めておく } } } class Signal { Random rnd = new Random(); // 乱数クラスのオブジェクトを準備する double[] abs = new double[1024]; // AGC処理用の信号レベルバッファ double[] temp = new double[1024]; // 交換用作業領域確保 double PI = 3.141592653589793238; // パイの値 double gainavarage; // AGCの時定数処理用変数 double realtemp, imagtemp; // 回転計算用の変数 double lastphase; // 直前の位相値 double max = 0; // 最大値を小さな値に初期化 double angle; // 乱数用の角度 double siglevel; // 信号の絶対値の最大値を求める double gain = 1.0; // デフォルトのゲイン値を設定 double maxgain = 1e6; // 最大ゲイン値は1000000 double agctime = 32; // AGCの時定数 double sm_level; //2018.8.22 // // I/Qの入れ替え // public Form1 form; //debug 2018.7.25 public void SwapData(double[] real, double[] imag) { Array.Copy(real, temp, 1024); // まずrealを保存 Array.Copy(imag, real, 1024); // imagをrealに上書き Array.Copy(temp, imag, 1024); // 保存していたrealをimagにコピー } // // 信号バッファをクリアする // public void ClearSignal(double[] real, double[] imag) { Array.Clear(real, 0, 1024); // 配列をクリアする Array.Clear(imag, 0, 1024); // 配列をクリアする } // // 2つの信号を足し算して合成する // public void AddSignal(double[] real, double[] imag, double[] localreal, double[] localimag) { for (int i = 0; i < 1024; i++) { real[i] += localreal[i]; // 加算して結果を格納 imag[i] += localimag[i]; // 加算して結果を格納 } } // // 2つの信号を掛け算して周波数変換を行なう // public void MultiplySignal(double[] real, double[] imag, double[] localreal, double[] localimag) { for(int i = 0; i < 1024; i++) { realtemp = real[i] * localreal[i] - imag[i] * localimag[i];//ベクトルの回転演算 imagtemp = real[i] * localimag[i] + imag[i] * localreal[i];//ベクトルの回転演算 real[i] = realtemp; // 結果を格納 imag[i] = imagtemp; // 結果を格納 } } // // 信号の絶対値(原点からの距離)を求めてその最大値を返す // public double GetAbs(double[] result, double[] real, double[] imag) { max = 0; // 最大値を小さな値に初期化 for(int i = 0; i < 1024; i++) { siglevel = Math.Sqrt(real[i] * real[i] + imag[i] * imag[i]); // ピタゴラスの定理で距離を計算 if(siglevel > max) max = siglevel; // 絶対値が大きければ最大値を更新 if(result != null) // 格納先変数が指定されていれば result[i] = siglevel; // 絶対値を格納する } return max; } // // 信号の位相(real軸からの角度)を求める // public void GetPhase(double[] result, double[] real, double[] imag) { for(int i = 0; i < 1024; i++) { result[i] = Math.Atan2(real[i], imag[i]); // ArcTanを求める if(result[i] < 0) // -3.14から0なら result[i] += 2 * PI; // 3.14から6.28に変換 } } // // 信号の周波数成分を求める // public void GetDivision(double[] result) { double div; for(int i = 0; i < 1024; i++) { div = result[i] - lastphase; // 直前の値との差分をとる lastphase = result[i]; // 直前の値を最新の値にアップデート if(div > PI) // 差分が180度以上なら0度を右回りで通過したので div -= 2 * PI; // 負の差分に補正する else if(div < -PI) // 差分がー180度以下なら0度を左回り通過なので div += 2 * PI; // 正の値に補正 result[i] = div / PI; // ±1の範囲に結果を収め格納する } } // // 信号に定数を掛けて増幅/減衰する // public void MulConst(double[] real, double[] imag, double gain) { for(int i = 0; i < 1024; i++) { real[i] *= gain; // gainを掛ける imag[i] *= gain; // gainを掛ける } } // // 信号に変調信号を掛けて振幅変調する // public void AmMod(double[] real, double[] imag, double[] af) { for(int i = 0; i < 1024; i++) { real[i] *= af[i] / 2 + 0.5; // 実部に振幅成分を掛ける imag[i] *= af[i] / 2 + 0.5; // 虚部に掛ける±1を0から1に変換する } } // // 信号を変調信号に従って周波数変調する // public void FmMod(double[] real, double[] imag, double[] af) { double modreal, modimag, a, b; for(int i = 0; i < 1024; i++) { angle = angle + af[i]; // 積分して位相変調すれば周波数変調になる if(angle > 2 * PI) // もし2πを超えたら angle -= 2 * PI; // 2π以下にしておく if(angle < 0) // もし負になったら angle += 2 * PI; // 0から2πの範囲内にしておく modreal = Math.Cos(angle); // 現在の積分値に対応する位相信号を求める modimag = Math.Sin(angle); // 現在の積分値に対応する位相信号を求める a = real[i] * modreal - imag[i] * modimag; // キャリアを回転させる b = real[i] * modimag + imag[i] * modreal; // キャリアを回転させる real[i] = a; // 実部を格納 imag[i] = b; // 虚部を格納 } } // // AGC処理を行なう // // public void ApplyAgc(double[] real, double[] imag, double level) public double ApplyAgc(double[] real, double[] imag, double level) { gain = 1.0; // デフォルトのゲイン値を設定 if(level != 0.0) // 0による除算を避ける為の処理 { gain = 0.1 / level; // 最大振幅が0.1になるようなゲインを算出 if(gain > maxgain) // ゲインが最大値より大きいか? gain = maxgain; // ゲインを最大値に制限 } if(gain < gainavarage / agctime) // 強い信号レベルの場合には gainavarage = gain * agctime; // すぐにAGCレベルを反映させる else { // 弱い信号レベルなら gainavarage = gainavarage - gainavarage / agctime + gain; // ゆっくりゲインを上昇させる gain = gainavarage / agctime; // AGCに時定数を持たせる } MulConst(real, imag, gain); // AGCを適用する sm_level = Math.Log10(gain) * 20; //Sメーター表示用 return sm_level; //2018.8.22 } // // テスト用にホワイトノイズを発生させる // public void CreateNoise(double[] real, double[] imag, double level) { for(int i = 0; i < 1024; i++) { angle = rnd.Next(32767) * 2 * PI / 32767; // 角度をランダムで生成 real[i] = Math.Cos(angle) * level; // その角度の信号を作成 imag[i] = Math.Sin(angle) * level; // その角度の信号を作成 } } } }