SoDaRadio-5.0.3-master:8901fb5
BaseBandTX.cxx
Go to the documentation of this file.
1 /*
2  Copyright (c) 2012, Matthew H. Reilly (kb1vc)
3  All rights reserved.
4 
5  FM modulator features based on code contributed by and
6  Copyright (c) 2014, Aaron Yankey Antwi (aaronyan2001@gmail.com)
7 
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions are
10  met:
11 
12  Redistributions of source code must retain the above copyright
13  notice, this list of conditions and the following disclaimer.
14  Redistributions in binary form must reproduce the above copyright
15  notice, this list of conditions and the following disclaimer in
16  the documentation and/or other materials provided with the
17  distribution.
18 
19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 
32 #include "BaseBandTX.hxx"
33 #include "BaseBandRX.hxx"
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include "OSFilter.hxx"
40 #include "ReSamplers625x48.hxx"
41 #include <cstdlib>
42 
43 // Don't need these anymore #include "SoDa_tx_filter_tables.hxx"
44 
46  CmdMBox * _cmd_stream,
47  AudioIfc * _audio_ifc
48  ) : SoDa::SoDaThread("BaseBandTX")
49 {
50  debug_mode = false;
51  debug_ctr = 0;
52  tx_stream = _tx_stream;
53 
54  cmd_stream = _cmd_stream;
56 
58  tx_buffer_size = params->getRFBufferSize();
59 
60  // create the interpolator.
62 
63  // create the audio stream.
64  // borrow the stream from the BaseBandRX side. ?
65  // pa_stream = _pa_stream;
66  double srate = params->getAudioSampleRate();
67 
68  // now setup the FM deviation
69  nbfm_deviation = 2.0 * M_PI * 2.5e3 / srate; // 2.5 kHz max deviation
70  wbfm_deviation = 2.0 * M_PI * 75.0e3 / srate; // 75 kHz max deviation
71  fm_phase = 0.0;
72 
73  audio_ifc = _audio_ifc;
74 
75  tx_stream_on = false;
76 
77  // create the IQ buffer.
78  audio_IQ_buf = new std::complex<float>[8*audio_buffer_size];
79 
80  // create the Hilbert transformer
82 
83  // create the noise buffer
84  noise_buffer = new float[audio_buffer_size];
85  // and initialize it.
86  // use the same random init every time -- counterintuitive, but
87  // I want this to be "repeatable" noise.
88  srandom(0x92314159);
89  for(unsigned int i = 0; i < audio_buffer_size; i++) {
90  float rfl = ((float) random()) / ((float) RAND_MAX);
91  rfl = rfl - 0.5; // make the mean 0
92  noise_buffer[i] = rfl * 0.5;
93  }
94 
95  // clear the noise enable
96  tx_noise_source_ena = false;
97 
98  // setup the audio filter for the tx audio input...
99  tx_audio_filter = new SoDa::OSFilter(80.0, 150.0, 2300.0, 2400.0,
100  512, 1.0,
101  srate, audio_buffer_size);
102 
103  // enable the audio filter by default.
104  tx_audio_filter_ena = true;
105 
106  mic_gain = 0.8; // was ... 0.4;
107 
108  fm_mic_gain = 0.8;
109 }
110 
112 {
113  bool exitflag = false;
114  Command * cmd;
115  float audio_buf[audio_buffer_size];
116 
134  // first wake up the audio channel
135  audio_ifc->wakeIn();
136 
137  while(!exitflag) {
138  if((cmd = cmd_stream->get(cmd_subs)) != NULL) {
139  // process the command.
140  execCommand(cmd);
141  exitflag |= (cmd->target == Command::STOP);
142  cmd_stream->free(cmd);
143  }
144  else if (!tx_stream_on || cw_tx_mode) {
145  // read audio information and throw it away.
147  audio_ifc->recv(audio_buf, audio_buffer_size);
148  }
149  usleep(1000);
150  }
151  else {
152  // If we're in TX mode that isn't CW....
153  // get an input audio buffer.
155  audio_ifc->recv(audio_buf, audio_buffer_size);
156  SoDaBuf * txbuf = NULL;
157  float * audio_tx_buffer = audio_buf;
158 
159  if(tx_noise_source_ena) {
160  audio_tx_buffer = noise_buffer;
161  }
162 
163  // If we're using NOISE, we don't want to overwrite the
164  // noise buffer with a filtered noise sequence. Instead,
165  // if we're using NOISE and filtering, we'll dump the
166  // filters into the audio buffer, then point back to
167  // the audio buffer.
168  // If we aren't using NOISE, then this is all hunky dory too.
169  // If we're using NOISE and we aren't filtering, then audio_tx_buffer
170  // still points to the NOISE buffer.
171  if(tx_audio_filter_ena) {
172  tx_audio_filter->apply(audio_tx_buffer, audio_buf);
173  audio_tx_buffer = audio_buf;
174  }
175 
176  if(tx_mode == SoDa::Command::USB) {
177  txbuf = modulateAM(audio_tx_buffer, audio_buffer_size, true, false);
178  }
179  else if(tx_mode == SoDa::Command::LSB) {
180  txbuf = modulateAM(audio_tx_buffer, audio_buffer_size, false, true);
181  }
182  else if(tx_mode == SoDa::Command::AM) {
183  txbuf = modulateAM(audio_tx_buffer, audio_buffer_size, false, false);
184  }
185  else if(tx_mode == SoDa::Command::NBFM) {
186  txbuf = modulateFM(audio_tx_buffer, audio_buffer_size, nbfm_deviation);
187  }
188  else if(tx_mode == SoDa::Command::WBFM) {
189  txbuf = modulateFM(audio_tx_buffer, audio_buffer_size, wbfm_deviation);
190  }
191  if(txbuf != NULL) {
192  tx_stream->put(txbuf);
193  }
194  }
195  usleep(1000);
196  }
197  }
198 }
199 
201  unsigned int len,
202  bool is_usb,
203  bool is_lsb)
204 {
205  (void) len;
208 
209  if(is_usb || is_lsb) {
212  hilbert->apply(audio_buf, audio_IQ_buf, is_lsb, mic_gain);
213  }
214  else {
217  unsigned int i;
218  for(i = 0; i < audio_buffer_size; i++) {
219  audio_IQ_buf[i] = std::complex<float>(audio_buf[i], 0.0) * mic_gain;
220  }
221  }
222 
223 
224 
227  SoDa::SoDaBuf * txbuf = tx_stream->alloc();
228  if(txbuf == NULL) {
229  txbuf = new SoDaBuf(tx_buffer_size);
230  }
231  if(txbuf->getComplexLen() < tx_buffer_size) {
232  throw(new SoDa::SoDaException("Transmit signal buffer was a bad size.", this));
233  }
234 
237 
239  return txbuf;
240 }
241 
242 
243 SoDa::SoDaBuf * SoDa::BaseBandTX::modulateFM(float *audio_buf, unsigned int len, double deviation)
244 {
245  (void) len;
246  unsigned int i;
247 
248  for(i=0; i < audio_buffer_size; i++) {
249  double audio_amp = audio_buf[i] * fm_mic_gain;
250 
251  // apply a little clipping here.. better to sound
252  // bad than to bleed into the neighboring channel.
253  if(fabs(audio_amp) > 1.0) {
254  audio_amp = (audio_amp > 0.0) ? 1.0 : -1.0;
255  }
256 
257  fm_phase += deviation * audio_amp;
258 
259  while (fm_phase > (float)(M_PI))
260  fm_phase -= (float)(2.0 * M_PI);
261  while (fm_phase < (float)(-M_PI))
262  fm_phase += (float)(2.0 * M_PI);
263 
264  double oq, oi;
265  sincos(fm_phase, &oi, &oq);
266  audio_IQ_buf[i] = std::complex<float>(oi,oq);
267  }
268 
269  SoDa::SoDaBuf * txbuf = tx_stream->alloc();
270  if(txbuf == NULL){
271  txbuf = new SoDaBuf(tx_buffer_size);
272  }
273 
274  if(txbuf->getComplexLen() < tx_buffer_size){
275  throw(new SoDa::SoDaException("FM: Transmit signal buffer was a bad size.",this));
276  }
277  // Upsample the IQ audio (at 48KS/s) to the RF sample rate of 625 KS/s
278  interpolator->apply(audio_IQ_buf, txbuf->getComplexBuf());
279 
280  // Pass the newly created and filled buffer back to the caller
281  return txbuf;
282 }
283 
284 
286 {
287 
288  switch (cmd->target) {
290  // we only care about CW mode or NOT CW mode.
293  cw_tx_mode = true;
294  }
295  else {
296  cw_tx_mode = false;
297  }
298  break;
299  case SoDa::Command::TX_STATE: // SET TX_ON
300  // transition from RX to TX
301  if(cmd->iparms[0] == 3) {
302  tx_on = true;
303  if(!cw_tx_mode) {
304  // Wake up the audio interface.
305  // actually, never need to do this, as we never let it sleep
306  // audio_ifc->wakeIn();
307  tx_stream_on = true;
308  }
309  }
310 
311  // transition from TX to RX
312  if(cmd->iparms[0] == 0) {
313  tx_on = false;
314  if(tx_stream_on) {
315  // Put the audio interface to sleep
316  // and flush the input buffer
317  // Actually, never put the audio interface to sleep.
318  // always read from the input buffer.
319  // audio_ifc->sleepIn();
320  tx_stream_on = false;
321  }
322  }
323  break;
324  case SoDa::Command::TX_AF_GAIN: // set audio gain.
325  // audio gain is passed around as linear (in dB), but
326  // gets converted before we set the envelope power.
327  af_gain = powf(10.0, 0.1 * (cmd->dparms[0] - 50.0));
329  50 + 10.0 * log10(af_gain)));
330  break;
332  if(cmd->iparms[0] == Command::NOISE) {
333  tx_noise_source_ena = true;
334  debugMsg("TX Audio IN is NOISE.\n");
335  }
336  else {
337  tx_noise_source_ena = false;
338  debugMsg("TX Audio IN is MIC.\n");
339  }
340  break;
342  if(cmd->iparms[0] == 1) {
343  tx_audio_filter_ena = true;
344  debugMsg("TX Audio filter is enabled.\n");
345  }
346  else {
347  tx_audio_filter_ena = false;
348  debugMsg("TX Audio filter is disabled.\n");
349  }
350  break;
351 
352  default:
353  break;
354  }
355 }
356 
358 {
359  (void) cmd;
360 }
361 
363 {
364  (void) cmd;
365 }
366 
double wbfm_deviation
phase advance for 75kHz deviation
Definition: BaseBandTX.hxx:113
bool debug_mode
if true, print extra debug info
Definition: BaseBandTX.hxx:183
SoDa::SoDaBuf * modulateAM(float *audio_buf, unsigned int len, bool is_usb, bool is_lsb)
create an AM/SSB modulation envelope
Definition: BaseBandTX.cxx:200
Enable the TX audio bandpass filter (limit to 2.5 kHz) for SSB/AM/FM.
Definition: Command.hxx:424
The Thread baseclass for all SoDa thread objects.
Definition: SoDaBase.hxx:284
This is an overlap-and-save frequency domain implementation of a general FIR filter widget...
bool cw_tx_mode
if true, tx_mode is CW_L or CW_U
Definition: BaseBandTX.hxx:130
void debugMsg(const std::string &msg, unsigned int threshold=1)
Definition: Debug.hxx:64
turn transmitter on and off.
Definition: Command.hxx:197
bool tx_noise_source_ena
When this is TRUE, audio modes (USB,LSB,AM,NBFM,WBFM) use a noise source for input.
Definition: BaseBandTX.hxx:161
T * get(unsigned int subscriber_id)
Definition: MultiMBox.hxx:110
double fm_mic_gain
separate gain control for FM deviation....
Definition: BaseBandTX.hxx:114
int iparms[4]
integer parameters
Definition: Command.hxx:668
double getAudioSampleRate() const
Definition: Params.hxx:104
Set the modulation mode for the transmit chain.
Definition: Command.hxx:259
float af_gain
local microphone gain.
Definition: BaseBandTX.hxx:134
This class handles command line parameters and built-ins.
Definition: Params.hxx:42
The SoDa Exception class.
Definition: SoDaBase.hxx:217
In several places we have a real valued signal x(t) that needs to be converted to an analytic signal ...
BaseBandTX(Params *params, DatMBox *tx_stream, CmdMBox *cmd_stream, AudioIfc *audio_ifc)
constructor
Definition: BaseBandTX.cxx:45
CmdTarget target
the thing we&#39;re touching
Definition: Command.hxx:673
AudioIfc * audio_ifc
pointer to an AudioIfc object for the microphone input
Definition: BaseBandTX.hxx:137
The Buffer Class.
Definition: SoDaBase.hxx:72
unsigned int getRFBufferSize() const
Definition: Params.hxx:105
void execSetCommand(Command *cmd)
handle SET commands from the command channel
Definition: BaseBandTX.cxx:285
On receipt of a STOP command, all threads should exit their run loop.
Definition: Command.hxx:371
unsigned int tx_buffer_size
how long is the outbound RF buffer
Definition: BaseBandTX.hxx:127
Resampler for 48KHz to 625KHz data stream, built on rational ReSampler class.
std::complex< float > * getComplexBuf()
Return a pointer to the storage buffer of complex floats.
Definition: SoDaBase.hxx:133
virtual int recv(void *buf, unsigned int len, bool block=true)=0
recv – get a buffer of data from the audio input
This is a list of all the commands that can "do something" to one or more components in the SoDa radi...
Definition: Command.hxx:47
virtual bool recvBufferReady(unsigned int len)=0
recvBufferReady – is there enough space in the audio device recv buffer for a call from recv...
unsigned int getAFBufferSize() const
Definition: Params.hxx:106
SoDa::OSFilter * tx_audio_filter
TX audio filter.
Definition: BaseBandTX.hxx:171
ModulationType
modulation selector targets take one of these values
Definition: Command.hxx:478
bool tx_stream_on
if true, we are transmitting.
Definition: BaseBandTX.hxx:139
DatMBox * tx_stream
outbound RF stream to USRPTX transmit chain
Definition: BaseBandTX.hxx:117
SoDa::ReSample48to625 * interpolator
Upsample from 48KHz to 625KHz.
Definition: BaseBandTX.hxx:122
SoDa::SoDaBuf * modulateFM(float *audio_buf, unsigned int len, double deviation)
create a narrowband/wideband FM modulation envelope
Definition: BaseBandTX.cxx:243
void run()
the run method – does the work of the audio transmitter process
Definition: BaseBandTX.cxx:111
std::complex< float > * audio_IQ_buf
temporary storage for outbound modulation envelope
Definition: BaseBandTX.hxx:143
void free(T *m)
Definition: MultiMBox.hxx:118
unsigned int audio_buffer_size
length (in samples) of an input audio buffer
Definition: BaseBandTX.hxx:125
void apply(std::complex< float > *in, std::complex< float > *out)
Perform the resampling on a complex float buffer.
double dparms[4]
double float parameters
Definition: Command.hxx:669
unsigned int getComplexLen()
Return the number of complex float values in this buffer.
Definition: SoDaBase.hxx:101
Overlap-and-save filter class.
Definition: OSFilter.hxx:50
unsigned int apply(std::complex< float > *inbuf, std::complex< float > *outbuf, bool pos_sided=true, float gain=1.0)
Perform a hilbert transform on the INPHASE signal in the input buffer.
void execGetCommand(Command *cmd)
execute GET commands from the command channel
Definition: BaseBandTX.cxx:357
double nbfm_deviation
phase advance for 2.5kHz deviation.
Definition: BaseBandTX.hxx:112
Generic Audio Interface Class.
Definition: AudioIfc.hxx:44
unsigned int apply(std::complex< float > *inbuf, std::complex< float > *outbuf, float outgain=1.0)
run the filter on a complex input stream
Definition: OSFilter.cxx:334
CmdMBox * cmd_stream
command stream from UI and other units
Definition: BaseBandTX.hxx:118
float mic_gain
mic gain is adjustable, to make sure we aren&#39;t noxious.
Definition: BaseBandTX.hxx:181
SoDa::Command::ModulationType tx_mode
what modulation scheme? USB? LSB? CW_U?...
Definition: BaseBandTX.hxx:129
unsigned int cmd_subs
subscription ID for command stream
Definition: BaseBandTX.hxx:119
void put(T *m)
Definition: MultiMBox.hxx:97
SoDa::HilbertTransformer * hilbert
The hilbert transformer to create an analytic (I/Q) signal.
Definition: BaseBandTX.hxx:176
bool tx_on
set by Command::TX_STATE to on or off
Definition: BaseBandTX.hxx:131
void execRepCommand(Command *cmd)
handle Report commands from the command channel
Definition: BaseBandTX.cxx:362
bool tx_audio_filter_ena
There is an audio filter in the chain, by default.
Definition: BaseBandTX.hxx:166
virtual void wakeIn()=0
start the input stream
float * noise_buffer
This is a buffer that holds a set of "noise" samples (uniform random) for testing the TX audio chain...
Definition: BaseBandTX.hxx:155
void execCommand(Command *cmd)
Execute (dispatch) a message removed from the command stream to one of the basic Command handler func...
Definition: SoDaBase.hxx:335
Select the transmit chain audio input (for SSB, AM, and FM)
Definition: Command.hxx:419