'****************************************************************************** '* ToneGenerator * '* * '* This class implements a custom control to generate a sinusoidal tone. * '* The user can modify the frequency and output power in real time. * '* * '* We calculate the minimum samples of the tone that we need to generate a * '* continous signal with the given sampling and output frequency. * '* Then, each time the caller want to have a new fragment of the tone, we * '* repeat the calculated samples as many times as we need to fill the output * '* buffer. * '* * '* EQUITEL - C.V. February 2007 ALMDI * '****************************************************************************** Imports System.ComponentModel Imports System.Math Public Class ToneGenerator Private mInternalBuffer As Short() Private mBufSize As Integer Private mSampligFrequency As Integer = 8000 Private mFrequency As Integer = 1000 Private mPower As Double = -0 Private mSignalIsCalculated As Boolean = False #Region "Properties" _ Public Property SamplingFrequency() As Integer Get Return mSampligFrequency End Get Set(ByVal value As Integer) If value >= 8000 AndAlso value <= 96000 Then mSampligFrequency = value End If End Set End Property _ Public Property Frequency() As Integer Get Return mFrequency End Get Set(ByVal value As Integer) If value >= 100 AndAlso value <= mSampligFrequency / 2 Then mFrequency = value CtrlFrequency.Value = value lblFrequency.Text = Format(mFrequency / 1000, "0.0 kHz") ToneCalculation() End If End Set End Property _ Public Property Power() As Double Get Return mPower End Get Set(ByVal value As Double) If value >= -100 AndAlso value <= 0 Then mPower = value CtrlPower.Value = value lblPower.Text = Format(value, "0 dB") ToneCalculation() End If End Set End Property _ Public ReadOnly Property SignalIsCalculated() As Boolean Get Return mSignalIsCalculated End Get End Property _ Public ReadOnly Property NumCalculatedSamples() As Integer Get Return mBufSize End Get End Property #End Region Public Event OnCalculatedTone() Private Sub ToneCalculation() Dim i As Integer Dim t As Double Dim W As Double Dim Factor As Double Dim Vmax As Integer Dim M As Integer Dim NecessaryCycles As Integer Dim NecessarySamples As Integer 'We calculate the maximum common divider ot the two frequencies 'and the number of cycles and samples that we need to create a 'buffer that can be repeated to generate the tone. M = MCD(mSampligFrequency, mFrequency) NecessaryCycles = mFrequency / M NecessarySamples = mSampligFrequency / M '.. and do the calculation ReDim mInternalBuffer(NecessarySamples - 1) mBufSize = NecessarySamples W = 2 * 3.141592659 * mFrequency Factor = 20 ^ (mPower / (10 * 3)) Vmax = Int(32767 * Factor) For i = 0 To NecessarySamples - 1 t = i * (1 / mSampligFrequency) mInternalBuffer(i) = Vmax * Sin(W * t) Next mSignalIsCalculated = True RaiseEvent OnCalculatedTone() End Sub Private Function MCD(ByVal F1 As Integer, ByVal F2 As Integer) As Integer 'Maximum common divider of two integers calculated with the 'Euclide algorithm 'We assume that F1>F2 Dim X1 As Integer, X2 As Integer, X3 As Integer X1 = F1 X2 = F2 Do X3 = X1 Mod X2 If X3 = 0 Then Exit Do X1 = X2 X2 = X3 Loop Return X2 End Function Private Sub Power_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CtrlPower.Scroll Power = CtrlPower.Value End Sub Private Sub Frequency_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CtrlFrequency.Scroll CtrlFrequency.Value = Int(CtrlFrequency.Value / 100) * 100 Frequency = CtrlFrequency.Value End Sub Public Sub OutputPacket(ByRef Buffer As Short(), ByVal NumSamples As Integer) 'Returns a packet of the tone calculated Dim i As Integer Static mInternalBufferPointer As Integer = 0 If Not mSignalIsCalculated Then ToneCalculation() For i = 0 To NumSamples - 1 Buffer(i) = mInternalBuffer(mInternalBufferPointer) mInternalBufferPointer += 1 If mInternalBufferPointer = mBufSize Then mInternalBufferPointer = 0 End If Next End Sub End Class