Skip to content

Commit c8291c2

Browse files
committed
Added class radioCWModulator_F32 for Morse code
1 parent 2118306 commit c8291c2

2 files changed

Lines changed: 563 additions & 0 deletions

File tree

radioCWModulator_F32.cpp

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/*
2+
* radioCWModulator_F32.cpp
3+
*
4+
* Created: Bob larkin W7PUA March 2023
5+
*
6+
* License: MIT License. Use at your own risk.
7+
*/
8+
9+
#include "radioCWModulator_F32.h"
10+
#include "sinTable512_f32.h"
11+
12+
void radioCWModulator_F32::update(void) {
13+
float32_t keyData[128]; // CW key down and up, 0.0f and 1.0f
14+
float32_t modulateCW[128]; // Storage for data to modulate sine wave
15+
uint16_t index, i;
16+
float32_t a, b;
17+
audio_block_f32_t *blockOut;
18+
19+
blockOut = AudioStream_F32::allocate_f32(); // Output block
20+
if (!blockOut) return;
21+
22+
// A new character cannot enter sendBuffer during an interrupt, but
23+
// the state IDLE_CW can be created by some other state ending.
24+
// So it needs to be in the audio sample loop.
25+
26+
// We always generate CW at 12 ksps. The number of data points in this
27+
// generation varies to provide 128 output output points after
28+
// interpolation to 48 or 96 ksps.
29+
for(i=0; i<nSamplesPerUpdate; i++)
30+
{
31+
timeMsF += timeSamplesMs;
32+
timeMsI = (uint32_t)(0.5 + timeMsF);
33+
34+
if(!enableXmit) // Just leave the key up and no new characters
35+
{
36+
levelCW = 0.0f;
37+
goto noXmit;
38+
}
39+
40+
switch(stateCW)
41+
{
42+
case IDLE_CW:
43+
timeMsF = 0.0f;
44+
timeMsI = 0;
45+
if(((indexW-indexR)&0X1FF) > 0) // Get next char, if available
46+
{
47+
c = sendBuffer[indexR];
48+
if (c>95)
49+
c -= 64; // Convert lc to caps
50+
else
51+
c -= 32; // Move subscript to (0, 63)
52+
ic = mc[(int)c]; // Ch to morse code lookup
53+
if (c==27) // Long Dash from ';'
54+
{
55+
stateCW = LONG_DASH;
56+
levelCW = 1.0f;
57+
timeMsF = 0.0f;
58+
timeMsI = 0;
59+
}
60+
else if (ic==0X17) // A space character
61+
{
62+
stateCW = WORD_SPACE;
63+
levelCW = 0.0f;
64+
timeMsF = 0.0f;
65+
timeMsI = 0;
66+
}
67+
else if(ic>1 && (ic & 1)==0x01)
68+
{
69+
stateCW = DASH_CW;
70+
levelCW = 1.0f;
71+
timeMsF = 0.0f;
72+
timeMsI = 0;
73+
}
74+
else if(ic>1 && (ic & 1)==0X00)
75+
{
76+
stateCW = DOT_CW;
77+
levelCW = 1.0f;
78+
timeMsF = 0.0f;
79+
timeMsI = 0;
80+
}
81+
else if(ic==0X01)
82+
{
83+
stateCW = IDLE_CW;
84+
levelCW = 0.0f;
85+
}
86+
} // end, if new character
87+
break;
88+
case DASH_CW:
89+
if(timeMsI > dashCW) // Finished dash
90+
{
91+
levelCW = 0.0f;
92+
if(ic>1) ic >>= 1; // Shift right 1
93+
if(ic==1)
94+
stateCW = CHAR_SPACE;
95+
else
96+
stateCW = DOT_DASH_SPACE;
97+
timeMsF = 0.0f;
98+
timeMsI = 0;
99+
}
100+
break;
101+
case DOT_CW:
102+
if(timeMsI > dotCW)
103+
{
104+
levelCW = 0.0f;
105+
if(ic>1) ic >>= 1; // Shift right 1
106+
if(ic==1)
107+
stateCW = CHAR_SPACE;
108+
else
109+
stateCW = DOT_DASH_SPACE;
110+
timeMsF = 0.0f;
111+
timeMsI = 0;
112+
}
113+
break;
114+
case DOT_DASH_SPACE:
115+
if(timeMsI > ddCW) // Just finished
116+
{
117+
timeMsF = 0.0f;
118+
timeMsI = 0;
119+
if(ic>1 && (ic & 1)==0x01)
120+
{
121+
stateCW = DASH_CW;
122+
levelCW = 1.0f;
123+
}
124+
else if(ic>1 && (ic & 1)==0X00)
125+
{
126+
stateCW = DOT_CW;
127+
levelCW = 1.0f;
128+
}
129+
else
130+
{
131+
stateCW = IDLE_CW;
132+
levelCW = 0.0;
133+
}
134+
}
135+
break;
136+
case CHAR_SPACE:
137+
if(timeMsI > chCW+ddCW) // Just finished sending a character
138+
// chCW+ddCW sounds better than chCW to me.
139+
{
140+
indexR++; // Sending is ended, bump the read index
141+
indexR = indexR & 0X1FF; // Confine to (0, 511)
142+
timeMsF = 0.0f;
143+
timeMsI = 0;
144+
stateCW = IDLE_CW;
145+
break;
146+
}
147+
case WORD_SPACE:
148+
if(timeMsI > spCW) // Just finished sending a space
149+
{
150+
indexR++; // Sending is ended, bump the read index
151+
indexR = indexR & 0X1FF; // Confine to (0, 511)
152+
timeMsF = 0.0f;
153+
timeMsI = 0;
154+
stateCW = IDLE_CW;
155+
break;
156+
}
157+
case LONG_DASH:
158+
if(timeMsI > longDashCW) // Just finished sending a long dash
159+
{
160+
levelCW = 0.0f;
161+
stateCW = CHAR_SPACE;
162+
timeMsF = 0.0f;
163+
timeMsI = 0;
164+
break;
165+
}
166+
} // end switch
167+
noXmit:
168+
keyData[i] = levelCW;
169+
} // end, over all 128 times
170+
171+
arm_fir_f32(&GaussLPFInst, keyData, keyData, nSamplesPerUpdate);
172+
173+
// INTERPOLATE - Interpolate here to support higher sample rates,
174+
// while using the same spectral LPF. To this point we have 128, 32
175+
// or 16 "active" data points for 12, 48, or 96ksps.
176+
//
177+
// 0 1 2 3 4 5 6 7 8 9 i
178+
// 0 0 0 0 1 1 1 1 2 2 i/4
179+
// t 0 0 0 t 0 0 0 t 0 i==4*(i/4)
180+
//
181+
if(nSample > 1) // Only needs interpolation if >1
182+
{
183+
for(i=0; i<128; i++)
184+
{
185+
if( i==(nSample*(1/nSample)) )
186+
modulateCW[i]= keyData[i/nSample];
187+
else
188+
modulateCW[i] = 0.0f;
189+
}
190+
arm_fir_f32(&interpolateLPFInst, modulateCW, keyData, 128);
191+
}
192+
193+
// Interpolation is done, now amplitude modulate CW onto a sine wave.
194+
for (i=0; i < 128; i++)
195+
{
196+
phaseS += phaseIncrement;
197+
if (phaseS > 512.0f) phaseS -= 512.0f;
198+
index = (uint16_t) phaseS;
199+
float32_t deltaPhase = phaseS - (float32_t)index;
200+
// Read two nearest values of input value from the sine table
201+
a = sinTable512_f32[index];
202+
b = sinTable512_f32[index+1];
203+
blockOut->data[i] = magnitude*keyData[i]*(a+(b-a)*deltaPhase);
204+
}
205+
AudioStream_F32::transmit(blockOut);
206+
AudioStream_F32::release (blockOut);
207+
}

0 commit comments

Comments
 (0)