|
'******************************************************************************
|
'* 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"
|
|
<Category("Signal"), DefaultValue(GetType(Integer), "8000"), _
|
Description("Samplinf frequency of the generated signal")> _
|
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
|
|
<Category("Signal"), DefaultValue(GetType(Integer), "1000"), _
|
Description("Frequency of the tone")> _
|
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
|
|
<Category("Signal"), DefaultValue(GetType(Integer), "-100"), _
|
Description("Relative output power in decibels")> _
|
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
|
|
<Category("Signal"), _
|
Description("Indicates if the tone is calculated")> _
|
Public ReadOnly Property SignalIsCalculated() As Boolean
|
Get
|
Return mSignalIsCalculated
|
End Get
|
End Property
|
|
<Category("Signal"), _
|
Description("Number of samples calculated")> _
|
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
|