Alejandro Acuña
2024-12-18 44b33e24b644459038edd956cfce7345ce3236c1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
 
'******************************************************************************
'* 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