from htra_api import *
import matplotlib.pyplot as plt #本示例使用matplotlib绘图观察，实际使用DDC时无需使用该python库，有关matplotlib部分均可删除

###打开设备###
Status = 0          #函数的返回。
Device = c_void_p() #当前设备的内存地址。
DevNum = c_int(0)   #指定设备号。

BootProfile = BootProfile_TypeDef() #启动配置结构体，包括物理接口、供电方式等。
BootInfo = BootInfo_TypeDef()       #启动信息结构体，包括设备信息、USB速率等。

BootProfile.DevicePowerSupply = DevicePowerSupply_TypeDef.USBPortAndPowerPort #使用USB数据端口及独立电源端口双供电
BootProfile.PhysicalInterface = PhysicalInterface_TypeDef.USB                 #使用USB接口进行数据传输。

#使用网口设备配置ETH接口
#BootProfile.PhysicalInterface = PhysicalInterface_TypeDef.ETH #使用网口进行数据传输
#BootProfile.ETH_IPVersion = IPVersion_TypeDef.IPv4
#BootProfile.ETH_RemotePort = 5000
#BootProfile.ETH_ReadTimeOut = 10000
#BootProfile.ETH_IPAddress[0] = 192
#BootProfile.ETH_IPAddress[1] = 168
#BootProfile.ETH_IPAddress[2] = 1
#BootProfile.ETH_IPAddress[3] = 100

Status = dll.Device_Open(pointer(Device),DevNum,pointer(BootProfile),pointer(BootInfo)) #打开设备。
if(Status == 0):
    print("Device is opened successfully")
else:
    print("Return other errors Status = {:d}".format(Status))

###配置下发###
IQS_ProfileIn = IQS_Profile_TypeDef()  #IQS输入配置，包括起始频率、终止频率、RBW、参考电平等。
IQS_ProfileOut = IQS_Profile_TypeDef() #IQS输出配置。
StreamInfo = IQS_StreamInfo_TypeDef()  #当前配置下IQ数据信息，包括带宽,IQ单路采样率等。
IQStream = IQStream_TypeDef()          #存放IQ数据包数据，包括IQ数据、配置信息等。

dll.IQS_ProfileDeInit(pointer(Device),pointer(IQS_ProfileIn)) #初始化配置IQS模式下的相关参数。

IQS_ProfileIn.CenterFreq_Hz = 400e6                         #配置中心频率。
IQS_ProfileIn.RefLevel_dBm = 0                              #配置参考电平。
IQS_ProfileIn.DecimateFactor = 4                            #配置抽取倍数。
IQS_ProfileIn.DataFormat = DataFormat_TypeDef.Complex16bit  #配置IQ数据格式。
IQS_ProfileIn.TriggerSource = IQS_TriggerSource_TypeDef.Bus #配置触发源为内部总线触发。
IQS_ProfileIn.BusTimeout_ms = 5000
IQS_ProfileIn.TriggerMode = TriggerMode_TypeDef.FixedPoints
IQS_ProfileIn.TriggerLength = 600000                        #配置单次触发采集的点数。仅当TriggerMode设置为FixedPoints时生效。

Status = dll.IQS_Configuration(pointer(Device),pointer(IQS_ProfileIn),pointer(IQS_ProfileOut),pointer(StreamInfo)) #下发IQS模式的相关配置。
if(Status == 0):
    print("configuration delievery succeeded")
else:
    print("IQS_Configuration call is incorrect Status = {:d}".format(Status))

###获取数据###
AlternIQStream_data = (c_int16 * (StreamInfo.StreamSamples * 2))()  #创建存放IQ数据数组

#打开DSP功能
DSP = c_void_p()
dll.DSP_Open(pointer(DSP)) #打开DSP功能。

#打开DSP功能
DDC_ProfileIn = DSP_DDC_TypeDef()
DDC_ProfileOut = DSP_DDC_TypeDef()
IQStreamOut = IQStream_TypeDef()

dll.DSP_DDC_DeInit(pointer(DDC_ProfileIn)) 

DDC_ProfileIn.DDCOffsetFrequency = 0                  #配置DDC的频率偏移。
DDC_ProfileIn.DecimateFactor = 2000                   #配置DDC的抽取倍数。
DDC_ProfileIn.SamplePoints = StreamInfo.StreamSamples #配置采样点数。
DDC_ProfileIn.SampleRate = StreamInfo.IQSampleRate    #配置采样率。

dll.DSP_DDC_Configuration(pointer(DSP), pointer(DDC_ProfileIn),pointer(DDC_ProfileOut))#通过调用此函数下发DDC模式的相关配置。

IQToSpectrumIn = DSP_FFT_TypeDef()  #配置FFT模式参数。
IQToSpectrumOut = DSP_FFT_TypeDef() #反馈实际下发的FFT模式参数。
TracePoints = c_uint32(0)           #经过FFT后的频谱点数。

dll.DSP_FFT_DeInit(pointer(IQToSpectrumIn)) #初始化配置FFT模式的相关参数。

IQToSpectrumIn.Calibration = 0                                             #配置是否开启校准，0则不开校准，其他值开启。
IQToSpectrumIn.DetectionRatio = 2                                          #配置检波率。
IQToSpectrumIn.TraceDetector = TraceDetector_TypeDef.TraceDetector_PosPeak #配置检波方式。
IQToSpectrumIn.FFTSize = DDC_ProfileOut.SamplePoints                       #配置FFT点数。
IQToSpectrumIn.Intercept = 1                                               #配置截取率。
IQToSpectrumIn.SamplePts = DDC_ProfileOut.SamplePoints                     #配置采样点数。
IQToSpectrumIn.WindowType = Window_TypeDef.FlatTop                         #配置窗型。

RBWRatio = c_double(0) #此参数返回RBW比值，RBW = RBWRatio * StreamInfo.IQSampleRate。
dll.DSP_FFT_Configuration(pointer(DSP),pointer(IQToSpectrumIn),pointer(IQToSpectrumOut),pointer(TracePoints),pointer(RBWRatio))
Frequency = (c_double * TracePoints.value)()    #创建数组存放频率数据。
PowerSpec_dBm = (c_float * TracePoints.value)() #创建数组存放幅度数据。

#使用pyplt绘图
fig, ax = plt.subplots()
line, = ax.plot([], [], label='Spectrum') #初始化一个空线条
ax.set_title('IQSToSpectrum')
ax.set_xlabel('Frequency')
ax.set_ylabel('PowerSpec_dBm')

ax.set_xlim(IQS_ProfileIn.CenterFreq_Hz-StreamInfo.IQSampleRate/DDC_ProfileIn.DecimateFactor/2, IQS_ProfileIn.CenterFreq_Hz+StreamInfo.IQSampleRate/DDC_ProfileIn.DecimateFactor/2)
ax.set_ylim(-160, 25)
ax.legend()
# 显示图形，但不在循环结束后关闭它（交互式模式）
plt.ion()
plt.show()

try:
    while(True):

        Status = dll.IQS_BusTriggerStart(pointer(Device)) #调用IQS_BusTriggerStart触发设备。若触发源为外部触发，则不需要调用此函数。

        for i in range(0,StreamInfo.PacketCount):
            dll.IQS_GetIQStream_PM1(pointer(Device),pointer(IQStream)) #获取IQ数据包、触发信息、I路数据最大值及最大值数组下标

            if i != StreamInfo.PacketCount - 1: #不是最后一包时的拼接点数
                samples_to_copy = StreamInfo.PacketSamples * 2
            else: #当是最后一包时确认拼接点数
                samples_to_copy = (StreamInfo.StreamSamples % StreamInfo.PacketSamples) * 2 if StreamInfo.StreamSamples % StreamInfo.PacketSamples != 0 else StreamInfo.PacketSamples * 2

            AlternIQStream_type = c_int16 * (samples_to_copy)                             #定义数组类型以取出IQStream.AlternIQStream中数据。
            source_data_ptr = cast(IQStream.AlternIQStream, POINTER(AlternIQStream_type)) #指针类型转换。
            source_data = source_data_ptr.contents                                        #获取数据存放至source_data。
            for j in range(samples_to_copy):                                              #循环将数据拼接至AlternIQStream_data中。
                AlternIQStream_data[i * (StreamInfo.PacketSamples * 2) + j] = source_data[j]

        raw_data_ptr = pointer(AlternIQStream_data)                     #创建指针指向拼接后的数据
        IQStream.AlternIQStream = cast(raw_data_ptr, POINTER(c_void_p)) #将原始数据指针转换为void*类型的指针
        IQStream.IQS_StreamInfo.PacketSamples = IQS_ProfileIn.TriggerLength

        dll.DSP_DDC_Execute(pointer(DSP), pointer(IQStream), pointer(IQStreamOut))
        dll.DSP_FFT_IQSToSpectrum(pointer(DSP), pointer(IQStreamOut), Frequency, PowerSpec_dBm) #执行IQ转频谱函数。
        dll.DSP_DDC_Reset(pointer(DSP))                                                         #重置DDC缓存避免抖动

        line.set_xdata(Frequency)     #绘制频谱图
        line.set_ydata(PowerSpec_dBm)
        
        ax.relim()
        ax.autoscale_view()
        plt.draw()
        plt.pause(0.01)
        
except KeyboardInterrupt:
    print("Stopped by user with Ctrl+C")
finally:
    dll.Device_Close(pointer(Device))
    print("Device closed.")

    