using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// 波形定義
enum WShape : int
{
    Sin, Rectangle, Triangle
}

namespace TUSB0214DAMSampleApp
{
    static class Etc
    {       
        // 周波数値から周波数を表す文字列を生成する
        public static string GetFrequencyStr(double freq)
        {
            if (freq > 1.0E6)// 1MHzより大はエラー
                return "Freq. OVF";
            else if (freq == 1.0E6)// 1MHzの時
                return "1.00000MHz";
            else if (freq >= 1.0E5)// 100KHz以上
                return ((freq / 1.0E3).ToString("F3") + "kHz");
            else if (freq >= 1.0E4)// 10KHz以上
                return ((freq / 1.0E3).ToString("F4") + "kHz");
            else if (freq >= 1.0E3)// 1KHz以上
                return ((freq / 1.0E3).ToString("F5") + "kHz");
            else if (freq >= 1.0E2)// 100Hz以上
                return (freq.ToString("F3") + "Hz");
            else if (freq >= 1.0E1)// 10Hz以上
                return (freq.ToString("F4") + "Hz");
            else if (freq >= 1.0)// 1Hz以上
                return (freq.ToString("F5") + "Hz");
            else if (freq >= 0.1)// 0.1Hz以上
                return (freq.ToString("F6") + "Hz");
            else if (freq >= 0.01)// 0.01Hz以上
                return (freq.ToString("F7") + "Hz");
            else
                return (freq.ToString("F7") + "Hz");
        }

        // クロックの要求周波数からカウンタの設定値を
        // 算出する
        // freq:要求周波数
        // kilo:周波数を1000倍するかどうか
        // setn:設定すべき数値
        // 範囲外ならfalse
        public static bool GetClockSetting(double freq, bool kilo, out uint setn)
        {
            if (kilo)
                freq *= 1000.0;
            double tmpd = Math.Round(100.0E6 / freq, MidpointRounding.AwayFromZero);
            setn = 0;
            if (tmpd < 100.0)
                return false;
            else if (tmpd > 4297296925.0)
                return false;
            setn = (uint)tmpd;
            return true;
        }

        // 波形イメージの要求周波数から波形に必要なデータ数算出する
        public static bool GetWaveLength(double freq, bool kilo, double clkfreq, out int len)
        {
            if (kilo)
                freq *= 1000.0;
            // 四捨五入して整数化
            len = (int)Math.Round(clkfreq / freq, MidpointRounding.AwayFromZero);
            // 波形個数が2以上、メモリ長以内になければエラー
            if ((len < 2) || (len > 262144))
                return false;
            else
                return true;
        }

        // クロックの設定値から実際に生成される周波数を
        // 算出する
        public static double GetActualFrequency(uint setn)
        {
            if (setn == 0)
                return 0.0;
            double tmpd = 100.0E6 / (double)setn;
            return tmpd;
        }

        // 電圧レベルがレンジ内であるかどうか確認する
        public static bool VoltRangeChk(RangeSel range, double volt)
        {
            double[] VRangeMax = new double[]// レンジ毎の最大値
            {
                9.9997, //±10Vレンジ
                4.9998, //±5Vレンジ
                2.4999, //±2.5Vレンジ
                1.25,   //±1.25Vレンジ
                9.9998, //10Vレンジ
                4.9999, //5Vレンジ
                2.5    //2.5Vレンジ
                
            };
            double[] VRangeMin = new double[]// レンジ毎の最小値
            {
                -10.0,  //±10Vレンジ
                -5.0,   //±5Vレンジ
                -2.5,   //±2.5Vレンジ
                -1.25,  //±1.25Vレンジ
                0.0,    //10Vレンジ
                0.0,    //5Vレンジ
                0       //2.5Vレンジ

            };

            int tmprange = (int)range;
            if (VRangeMax[tmprange] < volt)
                return false;
            if (VRangeMin[tmprange] > volt)
                return false;
            return true;
        }

        // 電圧値(double)からDACに設定すべき値を算出する
        public static ushort DoubleToDacCode(double dat, RangeSel range)
        {
            double tmpd;
            switch (range)
            {
                case RangeSel.Bipol10: tmpd = (dat + 10.0) / 20.0 * 16384; break;
                case RangeSel.Bipol5: tmpd = (dat + 5.0) / 10.0 * 16384; break;
                case RangeSel.Bipol2_5: tmpd = (dat + 2.5) / 5.0 * 16384; break;
                case RangeSel.Bipol1_25: tmpd = (dat + 1.25) / 2.5 * 16384; break;
                case RangeSel.Unipol10: tmpd = dat / 10.0 * 16384; break;
                case RangeSel.Unipol5: tmpd = dat / 5.0 * 16384; break;
                case RangeSel.Unipol2_5: tmpd = dat / 2.5 * 16384; break;
                
                default: tmpd = 0.0; 
                break;
            }
            return tmpd.ToUShort();
        }

        // DACの値(ushort)から相当する電圧(double)を算出する
        public static double DacCodeToVolt(ushort dat, RangeSel range)
        {
            switch(range)
            {
                case RangeSel.Bipol10: return ((double)dat / 16384 * 20.0 - 10.0);
                case RangeSel.Bipol5: return ((double)dat / 16384 * 10.0 - 5.0);
                case RangeSel.Bipol2_5: return ((double)dat / 16384 * 5.0 - 2.5);
                case RangeSel.Bipol1_25: return ((double)dat / 16384 * 2.5 - 1.25);
                case RangeSel.Unipol10: return ((double)dat / 16384 * 10.0);
                case RangeSel.Unipol5: return ((double)dat / 16384 * 5.0);
                case RangeSel.Unipol2_5: return ((double)dat / 16384 * 2.5);
                
                default: return 0.0;
            }
        }

        // 波形イメージを生成する
        public static void WaveImageGen(WShape shape, RangeSel range, double offset, double amp, ref ushort[] wimage, int len)
        {
            switch (shape)
            {
                case WShape.Sin:// sin
                    for (int i = 0; i < len; i++)
                    {
                        wimage[i] = DoubleToDacCode(offset + amp * Math.Sin(2 * Math.PI * (double) i / (double) len), range);
                    }
                    break;
                case WShape.Rectangle:// rectangle
                    ushort HD = DoubleToDacCode(offset + amp, range);
                    ushort LD = DoubleToDacCode(offset - amp, range);
                    int mid = len / 2;
                    for (int i = 0; i < mid; i++)
                    {
                        wimage[i] = HD;
                    }
                    for (int i = mid; i < len; i++)
                    {
                        wimage[i] = LD;
                    }
                    break;
                case WShape.Triangle:// triangle
                    double diff = (amp * 4.0) / (double)len;
                    int Deg90 = len / 4;
                    int Deg270 = len * 3 / 4;
                    for (int i = 0; i < Deg90; i++)
                    {
                        wimage[i] = DoubleToDacCode(offset + diff * (double)i, range);
                    }
                    for (int i = Deg90; i < Deg270; i++)
                    {
                        wimage[i] = DoubleToDacCode(offset + 2.0 * amp - diff * (double)i, range);
                    }
                    for (int i = Deg270; i < len; i++)
                    {
                        wimage[i] = DoubleToDacCode(offset - 4.0 * amp + diff * (double)i, range);
                    }
                    break;
                default:
                    for (int i = 0; i < len; i++)
                        wimage[i] = 0;
                    break;
            }
        }

    }
}
