不韋 呂
/
SDR_AM_Rx_CIC
AM中波放送用SDR.CICフィルタのみを使用.CQ出版社「トランジスタ技術」誌,2021年4月号に掲載
Revision 1:30d9fb51dec1, committed 2019-09-23
- Comitter:
- MikamiUitOpen
- Date:
- Mon Sep 23 07:32:10 2019 +0000
- Parent:
- 0:6906f8616429
- Child:
- 2:4bec6b2be809
- Commit message:
- 2
Changed in this revision
--- a/Cic3Stage.hpp Mon Sep 16 14:20:17 2019 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -//------------------------------------------------------------- -// ダウンサンプリングで使う3段の CIC フィルタ用クラス -// -// 2019/04/30, Copyright (c) 2019 MIKAMI, Naoki -//------------------------------------------------------------- - -#include "mbed.h" - -#ifndef CIC3_FILTER_CLASS_HPP -#define CIC3_FILTER_CLASS_HPP - -namespace Mikami -{ - class Cic3Stage - { - public: - // コンストラクタ - // rate ダウンサンプリングの率,1/10 にダウンサンプリングする場合は 10 - // amp 二相発振器の出力の振幅 - Cic3Stage(int rate, float amp) - : G0_(1.0f/(amp*rate*rate*rate)), - vn1_(0), vn2_(0), vn3_(0), vn3M1_(0), yn1M1_(0), yn2M1_(0) {} - - // 累算 - void Accumulate(int16_t xn) - { - vn1_ += xn; // 累算1段目 - vn2_ += vn1_; // 累算2段目 - vn3_ += vn2_; // 累算3段目 - } - - // 差分 - float Difference() - { - int32_t yn1 = vn3_ - vn3M1_; // 差分1段目 - int32_t yn2 = yn1 - yn1M1_; // 差分2段目 - int32_t yn3 = yn2 - yn2M1_; // 差分3段目 - - vn3M1_ = vn3_; // 現在の値を保存,差分1段目 - yn1M1_ = yn1; // 現在の値を保存,差分2段目 - yn2M1_ = yn2; // 現在の値を保存,差分3段目 - - return G0_*yn3; - } - - private: - const float G0_; - int32_t vn1_, vn2_, vn3_; // 累算で使う変数 - int32_t vn3M1_, yn1M1_, yn2M1_; // 差分で使う変数 - - // コピー・コンストラクタ禁止のため - Cic3Stage(const Cic3Stage&); - // 代入演算子禁止のため - Cic3Stage& operator=(const Cic3Stage&); - }; -} -#endif // CIC3_FILTER_CLASS_HPP -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDR_Library/Cic3Stage.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -0,0 +1,57 @@ +//------------------------------------------------------------- +// ダウンサンプリングで使う3段の CIC フィルタ用クラス +// +// 2019/04/30, Copyright (c) 2019 MIKAMI, Naoki +//------------------------------------------------------------- + +#include "mbed.h" + +#ifndef CIC3_FILTER_CLASS_HPP +#define CIC3_FILTER_CLASS_HPP + +namespace Mikami +{ + class Cic3Stage + { + public: + // コンストラクタ + // rate ダウンサンプリングの率,1/10 にダウンサンプリングする場合は 10 + // amp 二相発振器の出力の振幅 + Cic3Stage(int rate, float amp) + : G0_(1.0f/(amp*rate*rate*rate)), + vn1_(0), vn2_(0), vn3_(0), vn3M1_(0), yn1M1_(0), yn2M1_(0) {} + + // 累算 + void Accumulate(int16_t xn) + { + vn1_ += xn; // 累算1段目 + vn2_ += vn1_; // 累算2段目 + vn3_ += vn2_; // 累算3段目 + } + + // 差分 + float Difference() + { + int32_t yn1 = vn3_ - vn3M1_; // 差分1段目 + int32_t yn2 = yn1 - yn1M1_; // 差分2段目 + int32_t yn3 = yn2 - yn2M1_; // 差分3段目 + + vn3M1_ = vn3_; // 現在の値を保存,差分1段目 + yn1M1_ = yn1; // 現在の値を保存,差分2段目 + yn2M1_ = yn2; // 現在の値を保存,差分3段目 + + return G0_*yn3; + } + + private: + const float G0_; + int32_t vn1_, vn2_, vn3_; // 累算で使う変数 + int32_t vn3M1_, yn1M1_, yn2M1_; // 差分で使う変数 + + // コピー・コンストラクタ禁止のため + Cic3Stage(const Cic3Stage&); + // 代入演算子禁止のため + Cic3Stage& operator=(const Cic3Stage&); + }; +} +#endif // CIC3_FILTER_CLASS_HPP
--- a/SDR_Library/F446_ADC.cpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/F446_ADC.cpp Mon Sep 23 07:32:10 2019 +0000 @@ -66,4 +66,3 @@ TIM->CR1 = TIM_CR1_CEN; // TIM8 を有効にする } } -
--- a/SDR_Library/F446_ADC.hpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/F446_ADC.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -43,4 +43,3 @@ }; } #endif // ADC_F446_POLLING_HPP -
--- a/SDR_Library/F446_ADC_Intr.hpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/F446_ADC_Intr.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -43,4 +43,3 @@ }; } #endif // ADC_F446_INTERRUPT_HPP -
--- a/SDR_Library/F446_DAC.hpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/F446_DAC.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -4,6 +4,7 @@ // // 2019/02/18, Copyright (c) 2019 MIKAMI, Naoki //-------------------------------------------------------- + #include "mbed.h" #ifndef DAC_F446_SINGLE_HPP @@ -43,4 +44,3 @@ }; } #endif // DAC_F446_SINGLE_HPP -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDR_Library/FastATan.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -0,0 +1,77 @@ +//-------------------------------------------------------------- +// 高速低精度 arctan 計算 +// 係数はミニマックス近似で求めたもの +// ただし,誤差は絶対誤差で評価した +// +// 2019/09/18, Copyright (c) 2019 MIKAMI, Naoki +//-------------------------------------------------------------- + +#include "mbed.h" + +#ifndef FAST_ARCTAN_LOW_PRECISION_HPP +#define FAST_ARCTAN_LOW_PRECISION_HPP + +namespace Mikami +{ + inline float ATanPoly(float x); + + // 引数の与え方は,atan2() と同じ + float FastATan(float y, float x) + { + static const float PI = 3.1415926536f; // π + static const float PI_4 = PI/4.0f; // π/4 + static const float PI3_4 = 3.0f*PI/4.0f; // 3π/4 + static const float PI_2 = PI/2.0f; // π/2 + + if ( (x == 0.0f) && (y == 0.0f) ) return 0.0f; + if (y == 0.0f) + { + if (x > 0.0f) return 0.0f; // 0 + else return PI; // π + } + float abs_x = fabsf(x); + float abs_y = fabsf(y); + + if (abs_x == abs_y) + { + if (x > 0.0f) + { + if (y > 0.0f) return PI_4; // π/4 + else return -PI_4; // -π/4 + } + else + { + if (y > 0.0f) return PI3_4; // 3π/4 + else return -PI3_4; // -3π/4 + } + } + + if (abs_x > abs_y) // |θ|< π/4,3π/4<|θ|<π + { + float u = ATanPoly(y/x); + if (x > 0.0f) return u; + if (y > 0.0f) return u + PI; + else return u - PI; + } + else // π/4 <|θ|<3π/4 + { + float u = ATanPoly(x/y); + if (y > 0.0f) return -u + PI_2; + else return -u - PI_2; + } + } + + inline float ATanPoly(float x) + { + static const float A1 = 0.9992138f; // a1 + static const float A3 = -0.3211750f; // a3 + static const float A5 = 0.1462645f; // a5 + static const float A7 = -0.03898651f; // a7 + + float x2 = x*x; + float atanx = (((A7*x2 + A5)*x2 + A3)*x2 + A1)*x; + + return atanx; + } +} +#endif // FAST_ARCTAN_LOW_PRECISION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDR_Library/FirFastSymmetry.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -0,0 +1,55 @@ +//----------------------------------------------------------------- +// SDR で使う FIR フィルタ,係数:対称 +// ● 処理の高速化のため,入力信号のバッファのサイズを 256,データを指す +// インデックスを uint8_t 型としてリング・バッファを実現している. +// ● さらに,係数が対称であることを利用して,先に x[k] + x[ORDER-k] と +// いう加算を行ってから,係数の乗算を行うようにした. +// +// 2019/03/03, Copyright (c) 2019 MIKAMI, Naoki +//----------------------------------------------------------------- + +#include "mbed.h" + +#ifndef FIR_DIRECT_FAST_SYMMETRY_HPP +#define FIR_DIRECT_FAST_SYMMETRY_HPP +namespace Mikami +{ + class FirFastSymmetry + { + public: + // コンストラクタ + // order フィルタの次数 + // hk[] フィルタの係数 + FirFastSymmetry(int order, const float hk[]) + : ORDER_(order), hm_(hk), index_(255) + { for (int k=0; k<SIZE_; k++) un_[k] = 0; } + + // 入力信号をバッファへ書き込む + void Store(float xIn) { un_[++index_] = xIn; } + + // FIR フィルタの実行 + float Execute() + { + __IO uint8_t ptrM = index_; + __IO uint8_t ptrP = index_ - ORDER_; + float acc = 0; + for (int k=0; k<ORDER_/2; k++) + acc += hm_[k]*(un_[ptrM--] + un_[ptrP++]); + acc += hm_[ORDER_/2]*un_[ptrM]; + return acc; + } + + private: + static const int SIZE_ = 256; // 入力信号のバッファのサイズ + const int ORDER_; // フィルタの次数 + const float *const hm_; // フィルタの係数 + float un_[SIZE_]; // 入力信号のバッファ + __IO uint8_t index_; // 最新の入力信号を示すインデックス + + // コピー・コンストラクタ禁止のため + FirFastSymmetry(const FirFastSymmetry&); + // 代入演算子禁止のため + FirFastSymmetry& operator=(const FirFastSymmetry&); + }; +} +#endif // FIR_DIRECT_FAST_SYMMETRY_HPP
--- a/SDR_Library/Iir1st.hpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/Iir1st.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -1,7 +1,8 @@ //----------------------------------------------------------------- // 一次の IIR フィルタ +// y[n] = a1*y[n-1] + (1 - a1)*x[n] // -// 2019/09/03, Copyright (c) 2019 MIKAMI, Naoki +// 2019/09/21, Copyright (c) 2019 MIKAMI, Naoki //----------------------------------------------------------------- #include "mbed.h" @@ -36,4 +37,3 @@ }; } #endif // IIR_1ST_HPP -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDR_Library/IirDcCut.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -0,0 +1,44 @@ +//----------------------------------------------------------------- +// 直流分除去用 IIR フィルタ +// u[n] = a1*u[n-1] + x[n] +// y[n] = g0*(u[n] - u[n-1]) +// +// 2019/09/21, Copyright (c) 2019 MIKAMI, Naoki +//----------------------------------------------------------------- + +#include "mbed.h" + +#ifndef IIR_DC_CUT_HPP +#define IIR_DC_CUT_HPP +namespace Mikami +{ + class IirDcCut + { + public: + // コンストラクタ + // a1 フィルタの係数 + // g0 利得定数 + IirDcCut(float a1, float g0) + : A1_(a1), G0_(g0) { un1_ = 0; } + + // フィルタの実行 + float Execute(float xn) + { + float un = A1_*un1_ + xn; + float yn = (un - un1_)*G0_; + un1_ = un; + return yn; + } + + private: + const float A1_; // フィルタ係数 + const float G0_; // 利得定数 + float un1_; // 中間の値 + + // コピー・コンストラクタ禁止のため + IirDcCut(const IirDcCut&); + // 代入演算子禁止のため + IirDcCut& operator=(const IirDcCut&); + }; +} +#endif // IIR_DC_CUT_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDR_Library/InitialMessage.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -0,0 +1,42 @@ +//------------------------------------------------------------- +// 実行開始時のターミナルへメッセージの送信 +// +// 2019/09/17, Copyright (c) 2019 MIKAMI, Naoki +//------------------------------------------------------------- + +#include "mbed.h" +#pragma diag_suppress 870 // マルチバイト文字使用の警告抑制のため + +#include <string> + +#ifndef INITIAL_MESSAGE_HPP +#define INITIAL_MESSAGE_HPP +enum AmFm { AM, FM }; // メッセージが AM 用か FM 用かを指定する + +// str 2行目に表示するメッセージ +// af メーッセージが AM 用か FM 用かを指定 +// sp メーッセージを出力するシリアルポート +void InitialMessage(string str, AmFm af, Serial &sp) +{ + sp.printf("\r\n"); + if (af == AM) + { + sp.printf("SDR で AM 放送を受信します.\r\n"); + sp.printf((str + "\r\n").c_str()); + sp.printf("0: NHK 第1\r\n1: NHK 第2\r\n2: AFN Tokyo\r\n3: TBS ラジオ\r\n" + "4: 文化放送\r\n5: ニッポン放送\r\n6: ラジオ日本\r\n\n"); + sp.printf("'0' ~ '6' のキーで選局できます.\r\n"); + sp.printf("'0' ~ '6', 'Enter' キー以外は何も反応しません.\r\n"); + } + else + { + sp.printf("\r\nSDR で FM 復調を実行します.\r\n"); + sp.printf((str + "\r\n").c_str()); + sp.printf("0 か 1 を入力してください.\r\n"); + sp.printf("'0', '1', 'Enter' キー以外は何も反応しません.\r\n"); + } + + sp.printf("'Enter' キーは CR/LF するだけです.\r\n\n"); + sp.putc('0'); +} +#endif // INITIAL_MESSAGE_HPP
--- a/SDR_Library/QuadOscIir.hpp Mon Sep 16 14:20:17 2019 +0000 +++ b/SDR_Library/QuadOscIir.hpp Mon Sep 23 07:32:10 2019 +0000 @@ -71,4 +71,3 @@ }; } #endif // QUAD_PHASE_OSC_IIR_HPP -
--- a/main.cpp Mon Sep 16 14:20:17 2019 +0000 +++ b/main.cpp Mon Sep 23 07:32:10 2019 +0000 @@ -1,13 +1,12 @@ //------------------------------------------------------------- -// 中波 AM 放送用 SDR +// 中波 AM 放送用 SDR,CIC フィルタを使用 +// +// ダウンサンプリング(1/90)で使う LPF として CIC フィルタを使用 +// // 入力ピン: A1 // 出力ピン: A2 // -// ダウンサンプリングで使う LPF として CIC フィルタを使用 -// CIC フィルタの段数:3段 -// CIC フィルタをクラスにする -// -// 2019/09/16, Copyright (c) 2019 MIKAMI, Naoki +// 2019/09/18, Copyright (c) 2019 MIKAMI, Naoki //------------------------------------------------------------- #include "F446_ADC_Intr.hpp" // ADC @@ -15,6 +14,7 @@ #include "QuadOscIir.hpp" // 二相発振器 #include "Cic3Stage.hpp" // 3段 CIC フィルタ #include "Iir1st.hpp" // 一次の IIR フィルタ +#include "InitialMessage.hpp" // 最初のメーッセージ using namespace Mikami; #pragma diag_suppress 870 // マルチバイト文字使用の警告抑制のため @@ -97,14 +97,7 @@ int main() { - printf("\r\n"); - printf("SDR (CIC フィルタ使用) を実行します.\r\n"); - printf("0: NHK 第1\r\n1: NHK 第2\r\n2: AFN Tokyo\r\n3: TBS ラジオ\r\n" - "4: 文化放送\r\n5: ニッポン放送\r\n6: ラジオ日本\r\n\n"); - printf("'0' ~ '6' のキーで選局できます.\r\n"); - printf("'Enter' キーは CR/LF するだけです.\r\n"); - printf("'0' ~ '6', 'Enter' キー以外は何も反応しません.\r\n\n"); - pc_.putc('0'); + InitialMessage("ダウンサンプリングに CIC フィルタ使用.", AM, pc_); NVIC_SetPriority(ADC_IRQn, 0); // 最優先 NVIC_SetPriority(EXTI4_IRQn, 1); // 2番目に優先