SoDaRadio-5.0.3-master:8901fb5
UI.cxx
Go to the documentation of this file.
1 /*
2  Copyright (c) 2013, Matthew H. Reilly (kb1vc)
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are
7  met:
8 
9  Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11  Redistributions in binary form must reproduce the above copyright
12  notice, this list of conditions and the following disclaimer in
13  the documentation and/or other materials provided with the
14  distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "UI.hxx"
30 #include "version.h"
31 
32 const double SoDa::UI::spectrum_span = 200e3;
33 
34 SoDa::UI::UI(Params * params, CmdMBox * _cwtxt_stream,
35  DatMBox * _rx_stream, DatMBox * _if_stream,
36  CmdMBox * _cmd_stream, CmdMBox * _gps_stream) : SoDa::SoDaThread("UI")
37 {
38  // connect to our message streams.
39  cwtxt_stream = _cwtxt_stream;
40  rx_stream = _rx_stream;
41  if_stream = _if_stream;
42  cmd_stream = _cmd_stream;
43  gps_stream = _gps_stream;
44 
45  // subscribe to them.
49 
50  // create the network ports
51  // This UI object is a server.
54 
55  baseband_rx_freq = 144e6; // just a filler to avoid divide by zero.
56  spectrum_center_freq = 144.2e6;
57 
58  // create the spectrogram object -- it eats RX IF buffers and produces
59  // power spectral density plots.
60  spectrogram_buckets = 4 * 4096;
62 
63  // we also need an LO check spectrogram. In particular we want
64  // something with really bodacious resolution.
65  lo_spectrogram_buckets = 16384;
67  lo_spectrum = new float[lo_spectrogram_buckets * 4];
68  for(unsigned int i = 0; i < lo_spectrogram_buckets; i++) {
69  lo_spectrum[i] = 0.0;
70  }
71 
72  // Now how wide is a 200KHz wide chunk of spectrum, given
73  // spectrogram_buckets frequency buckets in the RF sample rate
74  double rxrate = params->getRXRate();
75  hz_per_bucket = rxrate / ((float) spectrogram_buckets);
76  required_spect_buckets = (int) (floor(0.5 + spectrum_span / hz_per_bucket));
77  lo_hz_per_bucket = rxrate / ((float) lo_spectrogram_buckets);
78 
79  // now allocate the buffer that we'll send to the UI
80  spectrum = new float[spectrogram_buckets * 4];
81  log_spectrum = new float[spectrogram_buckets * 4];
82  // make it a little large, and "zero" it out to account for walking off the end...
83  for(unsigned int i = 0; i < spectrogram_buckets * 4; i++) {
84  spectrum[i] = 1e-20;
85  log_spectrum[i] = -200.0;
86  }
87 
88  fft_send_counter = 0;
90  new_spectrum_setting = true;
91  fft_acc_gain = 0.9;
92  // we are not yet in lo check mode
93  lo_check_mode = false;
94 }
95 
97 {
99  hz_per_bucket));
108  spectrum_span,
109  ((double) required_spect_buckets)));
110 }
111 
113 {
114  delete server_socket;
115  delete wfall_socket;
116 }
117 
119 {
120  SoDa::Command * net_cmd, * ring_cmd;
121 
122  net_cmd = NULL;
123  ring_cmd = NULL;
124 
129  usleep(100000);
131 
133 
134  unsigned int socket_read_count = 0;
135  unsigned int socket_empty_count = 0;
136  unsigned int iter_count = 0;
137  bool new_connection = true; ;
138  while(1) {
139  iter_count++;
140  bool didwork = false;
141  bool got_new_netmsg = false;
142  // listen on the socket.
143 
144  if(server_socket->isReady()) {
145  if(new_connection) {
147 
148  std::string vers= (boost::format("%s GIT %s") % SoDaRadio_VERSION % SoDaRadio_GIT_ID).str();
149  SoDa::Command * vers_cmd = new SoDa::Command(Command::REP,
151  vers.c_str());
152  server_socket->put(vers_cmd, sizeof(SoDa::Command));
153  new_connection = false;
154  }
155 
156  if(net_cmd == NULL) {
157  net_cmd = new SoDa::Command();
158  }
159  int stat = server_socket->get(net_cmd, sizeof(SoDa::Command));
160  if(stat <= 0) {
161  socket_empty_count++;
162  }
163  else {
164  socket_read_count++;
165  got_new_netmsg = true;
166  }
167  }
168  else {
169  new_connection = true;
170  }
171 
172  // if there are commands arriving from the socket port, handle them.
173  if(got_new_netmsg) {
174  debugMsg(boost::format("UI got message [%s]\n") % net_cmd->toString());
175  cmd_stream->put(net_cmd);
176  didwork = true;
177  if(net_cmd->target == SoDa::Command::TX_CW_EMPTY) {
178  debugMsg("got TX_CW_EMPTY command from socket.\n");
179  }
180  if(net_cmd->target == SoDa::Command::STOP) {
181  // relay "stop" commands to the GPS unit.
183  break;
184  }
185  net_cmd = NULL;
186  }
187 
188  while((ring_cmd = cmd_stream->get(cmd_subs)) != NULL) {
189  if(ring_cmd->cmd == SoDa::Command::REP) {
190  server_socket->put(ring_cmd, sizeof(SoDa::Command));
191  }
192  // if(net_cmd->target == SoDa::Command::TX_CW_EMPTY) {
193  // debugMsg("send TX_CW_EMPTY report to socket.\n");
194  // }
195 
196  execCommand(ring_cmd);
197  cmd_stream->free(ring_cmd);
198  didwork = true;
199  }
200 
201  while((ring_cmd = gps_stream->get(gps_subs)) != NULL) {
202  if(ring_cmd->cmd == SoDa::Command::REP) {
203  server_socket->put(ring_cmd, sizeof(SoDa::Command));
204  }
205  execCommand(ring_cmd);
206  gps_stream->free(ring_cmd);
207  didwork = true;
208  }
209 
210 
211  // listen ont the IF stream
212  int bcount;
213  SoDaBuf * if_buf;
214  for(bcount = 0;
215  (bcount < 4) && ((if_buf = if_stream->get(if_subs)) != NULL);
216  bcount++) {
217  sendFFT(if_buf);
218  if_stream->free(if_buf);
219  }
220 
221  //
222  // if there are any socket listeners on the waterfall channel,
223  // clue them in.
224 
225  // if there are any socket listeners on the status channel,
226  // clue them in.
227 
228  // if there is nothing to do, sleep for a little while. --
229  // do small sleep, to speedup spectrum update.
230  if(!didwork) usleep(5000);
231  }
232 
233 
234  return;
235 }
236 
238 {
241  sizeof(SoDa::Command));
244  sizeof(SoDa::Command));
246  hz_per_bucket),
247  sizeof(SoDa::Command));
250  sizeof(SoDa::Command));
253  spectrum_span,
254  ((double) required_spect_buckets)),
255  sizeof(SoDa::Command));
256 
257 }
258 
259 
261 {
262  // when we get a SET SPEC_CENTER_FREQ
263  switch(cmd->target) {
265  spectrum_center_freq = cmd->dparms[0];
266  new_spectrum_setting = true;
268  break;
270  fft_acc_gain = 1.0 - (1.0 / ((double) cmd->iparms[0]));
271  new_spectrum_setting = true;
272  break;
274  fft_update_interval = 11 - cmd->iparms[0];
275  if(cmd->iparms[0] > 11) fft_update_interval = 0;
276  if(cmd->iparms[0] < 0) fft_update_interval = 11;
277  new_spectrum_setting = true;
278  debugMsg(boost::format("Updated SPEC_UPDATE_RATE = %d -> interval = %d\n")
279  % cmd->iparms[0] % fft_update_interval);
280  break;
281  default:
282  break;
283  }
284 }
285 
287 {
288  switch(cmd->target) {
289  case SoDa::Command::LO_OFFSET: // remember that we want to report
290  // the offset of the LO microwave oscillator on the next FFT event.
291  lo_check_mode = true;
292  fft_send_counter = 0;
293  break;
294  default:
295  break;
296  }
297 }
298 
300 {
301  switch(cmd->target) {
303  // save the front end baseband frequency
304  baseband_rx_freq = cmd->dparms[0];
305  break;
306  default:
307  break;
308  }
309 }
310 
311 
312 static unsigned int dbgctrfft = 0;
313 static bool first_ready = true;
314 static bool calc_max_first = true;
315 
317 {
318  fft_send_counter++;
319  dbgctrfft++;
320  if(!wfall_socket->isReady()) {
321  return;
322  }
323  if(first_ready == true) dbgctrfft = 0;
324  first_ready = false;
325 
326 
327  // Do an FFT on the buffer
328  // Note that we'll only send over on buffer every
329  // fft_update_interval times that we're called -- this will keep
330  // the IP traffic to something reasonable.
331  if(lo_check_mode) {
332  lo_spectrogram->apply_acc(buf->getComplexBuf(), buf->getComplexLen(), lo_spectrum, (fft_send_counter == 0) ? 0.0 : 0.1);
333  }
334  else {
337  }
338  new_spectrum_setting = false;
339  calc_max_first = false;
340 
341  float * slice = spectrum;
342 
343  if(!lo_check_mode) {
344  // find the right slice
345  int idx;
346  // first the index of the center point
347  // this is the bucket for the baseband rx freq
348  idx = (spectrogram_buckets / 2);
349  idx += (int) round((spectrum_center_freq - baseband_rx_freq) / hz_per_bucket);
350  // now we've got the index for the center.
351  // correct it to be the start...
352  idx -= required_spect_buckets / 2;
353  int sbuck_target = (int) spectrogram_buckets;
354  if((idx < 0) || (idx > sbuck_target)) {
355  slice = NULL;
356  }
357  else {
358  slice = &(spectrum[idx]);
359  }
360  }
361 
362  if(lo_check_mode && (fft_send_counter >= 8)) {
363  // scan the buffer. Then find the peak.
364  // scan from lo_spectrum midpoint minus 2KHz to plus 2KHz
365  float magmax = 0.0;
366  int maxi = 0;
367  int idxrange = ((int) (2000.0 / lo_hz_per_bucket));
368  int i, j;
369  for(i = -idxrange, j = (lo_spectrogram_buckets / 2) - idxrange; i < idxrange; i++, j++) {
370  std::complex<float> v = lo_spectrum[j];
371  float mag = v.real() * v.real() + v.imag() * v.imag();
372  if(mag > magmax) {
373  magmax = mag;
374  maxi = i;
375  }
376  }
377  lo_check_mode = false;
378  // send the report
379  double freq = ((float) maxi) * lo_hz_per_bucket;
380  debugMsg(boost::format("offset = %g\n") % freq);
382  freq));
389  spectrum_span,
390  ((double) required_spect_buckets)));
391  // send the end-of-calib command
393  0.0));
394  }
395  else if((fft_send_counter >= fft_update_interval) && (slice != NULL)) {
396  // send the buffer over to the XY plotter.
397  for(int i = 0; i < required_spect_buckets; i++) {
398  log_spectrum[i] = 10.0 * log10(slice[i] * 0.05);
399  }
400  wfall_socket->put(log_spectrum, sizeof(float) * required_spect_buckets);
401  fft_send_counter = 0;
402  calc_max_first = true;
403  float maxmag = 0.0;
404 
405  for(unsigned int ii = 0; ii < spectrogram_buckets; ii++) {
406  if(spectrum[ii] > maxmag) {
407  maxmag = spectrum[ii];
408  }
409  }
410  }
411 }
unsigned int gps_subs
Definition: UI.hxx:58
unsigned int spectrogram_buckets
Definition: UI.hxx:66
The Thread baseclass for all SoDa thread objects.
Definition: SoDaBase.hxx:284
low spectrum frequency range
Definition: Command.hxx:274
void debugMsg(const std::string &msg, unsigned int threshold=1)
Definition: Debug.hxx:64
static bool first_ready
Definition: UI.cxx:313
turn transmitter on and off.
Definition: Command.hxx:197
T * get(unsigned int subscriber_id)
Definition: MultiMBox.hxx:110
unsigned int cmd_subs
Definition: UI.hxx:58
This is an LO check command - use it for finding the actual microwave LO frequency.
Definition: Command.hxx:299
int get(void *ptr, unsigned int size)
Definition: UDSockets.hxx:75
void reportSpectrumCenterFreq()
Definition: UI.cxx:237
double spectrum_center_freq
Definition: UI.hxx:77
int iparms[4]
integer parameters
Definition: Command.hxx:668
Report when CW TX envelope buffer was empty (cmd enables report)
Definition: Command.hxx:242
This class handles command line parameters and built-ins.
Definition: Params.hxx:42
SoDa::UD::ServerSocket * wfall_socket
Definition: UI.hxx:62
all spec info in one call, cf, span, and buflen
Definition: Command.hxx:279
Spectrogram * lo_spectrogram
Definition: UI.hxx:68
CmdTarget target
the thing we&#39;re touching
Definition: Command.hxx:673
static bool calc_max_first
Definition: UI.cxx:314
freqency step
Definition: Command.hxx:276
CmdMBox * cwtxt_stream
Definition: UI.hxx:55
double getRXRate() const
Sample rates and all that other stuff are fixed.
Definition: Params.hxx:98
The Buffer Class.
Definition: SoDaBase.hxx:72
int required_spect_buckets
Definition: UI.hxx:79
CmdMBox * gps_stream
Definition: UI.hxx:55
float * log_spectrum
Definition: UI.hxx:81
float * spectrum
Definition: UI.hxx:81
On receipt of a STOP command, all threads should exit their run loop.
Definition: Command.hxx:371
double lo_hz_per_bucket
Definition: UI.hxx:70
void apply_acc(std::complex< float > *invec, unsigned int inveclen, float *outvec, float accumulation_gain=0.0)
Calculate the spectrogram from an input vector – add it to an accumulation buffer.
std::complex< float > * getComplexBuf()
Return a pointer to the storage buffer of complex floats.
Definition: SoDaBase.hxx:133
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
how many FFT samples contribute to a spectrum report
Definition: Command.hxx:281
Same effect as TX_TUNE_FREQ.
Definition: Command.hxx:115
double hz_per_bucket
Definition: UI.hxx:78
void run()
Each thread object must define its "run" loop.
Definition: UI.cxx:118
double baseband_rx_freq
Definition: UI.hxx:76
the center frequency (command from GUI)
Definition: Command.hxx:273
bool new_spectrum_setting
Definition: UI.hxx:82
SoDa::UD::ServerSocket * server_socket
Definition: UI.hxx:62
Set the RX front end 1st LO and the 2nd IF LO.
Definition: Command.hxx:79
void free(T *m)
Definition: MultiMBox.hxx:118
float fft_acc_gain
Definition: UI.hxx:84
CmdMBox * cmd_stream
Definition: UI.hxx:55
unsigned int fft_send_counter
Definition: UI.hxx:87
DatMBox * rx_stream
Definition: UI.hxx:56
CmdType cmd
the command type (SET, GET, REP)
Definition: Command.hxx:672
void updateSpectrumState()
Definition: UI.cxx:96
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
~UI()
Definition: UI.cxx:112
UI(Params *params, CmdMBox *cwtxt_stream, DatMBox *rx_stream, DatMBox *if_stream, CmdMBox *cmd_stream, CmdMBox *gps_stream)
Definition: UI.cxx:34
void sendFFT(SoDaBuf *buf)
Definition: UI.cxx:316
how many FFT samples between spectrum reports
Definition: Command.hxx:282
Report the SDR (SoDa server program) version info.
Definition: Command.hxx:344
static const double spectrum_span
Definition: UI.hxx:74
unsigned int fft_update_interval
Definition: UI.hxx:85
Tune the 3rd LO (in SoDa::USRPRX).
Definition: Command.hxx:96
Spectrogram * spectrogram
Definition: UI.hxx:65
number of samples in the buffer.
Definition: Command.hxx:277
float * lo_spectrum
Definition: UI.hxx:71
static unsigned int dbgctrfft
Definition: UI.cxx:312
unsigned int if_subs
Definition: UI.hxx:58
void execRepCommand(Command *cmd)
optional method that reports status or the result of some action.
Definition: UI.cxx:299
high spectrum frequency range
Definition: Command.hxx:275
void put(T *m)
Definition: MultiMBox.hxx:97
DatMBox * if_stream
Definition: UI.hxx:57
Spectrogram generates magnitude buffers from input sample stream.
Definition: Spectrogram.hxx:39
unsigned int lo_spectrogram_buckets
Definition: UI.hxx:69
void execGetCommand(Command *cmd)
optional method to handle "GET" commands – commands that request a response
Definition: UI.cxx:286
int put(const void *ptr, unsigned int size)
Definition: UDSockets.hxx:80
std::string getServerSocketBasename() const
Definition: Params.hxx:108
bool lo_check_mode
Definition: UI.hxx:90
std::string toString() const
return a string that displays the command
Definition: Command.cxx:230
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
void execSetCommand(Command *cmd)
optional method to handle "SET" commands – commands that set internal state in the object...
Definition: UI.cxx:260
this is a GET/REP command – BaseBandRX takes FFT centered around 0 and reports largest peak within 5...
Definition: Command.hxx:305