1 /* -*- c++ -*- */
2 /*
3  * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4  *           https://gqrx.dk/
5  *
6  * Copyright 2013 Alexandru Csete OZ9AEC.
7  *
8  * Gqrx is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3, or (at your option)
11  * any later version.
12  *
13  * Gqrx is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Gqrx; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street,
21  * Boston, MA 02110-1301, USA.
22  */
23 #include <cmath>
24 #include <cstdlib>
25 #include <iostream>
26 #include <QString>
27 #include <QStringList>
28 #include "remote_control.h"
29 #include "qtgui/dockrxopt.h"
30 
31 #define DEFAULT_RC_PORT            7356
32 #define DEFAULT_RC_ALLOWED_HOSTS   "::ffff:127.0.0.1"
33 
RemoteControl(QObject * parent)34 RemoteControl::RemoteControl(QObject *parent) :
35     QObject(parent)
36 {
37 
38     rc_freq = 0;
39     rc_filter_offset = 0;
40     bw_half = 740e3;
41     rc_lnb_lo_mhz = 0.0;
42     rc_mode = 0;
43     rc_passband_lo = 0;
44     rc_passband_hi = 0;
45     rc_program_id = "0000";
46     rds_status = false;
47     signal_level = -200.0;
48     squelch_level = -150.0;
49     audio_recorder_status = false;
50     receiver_running = false;
51     hamlib_compatible = false;
52 
53     rc_port = DEFAULT_RC_PORT;
54     rc_allowed_hosts.append(DEFAULT_RC_ALLOWED_HOSTS);
55 
56     rc_socket = 0;
57 
58     connect(&rc_server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
59 }
60 
~RemoteControl()61 RemoteControl::~RemoteControl()
62 {
63     stop_server();
64 }
65 
66 /*! \brief Start the server. */
start_server()67 void RemoteControl::start_server()
68 {
69     if (!rc_server.isListening())
70         rc_server.listen(QHostAddress::Any, rc_port);
71 }
72 
73 /*! \brief Stop the server. */
stop_server()74 void RemoteControl::stop_server()
75 {
76     if (rc_socket != 0) {
77         rc_socket->close();
78         rc_socket->deleteLater();
79         rc_socket = 0;
80     }
81 
82     if (rc_server.isListening())
83         rc_server.close();
84 
85 }
86 
87 /*! \brief Read settings. */
readSettings(QSettings * settings)88 void RemoteControl::readSettings(QSettings *settings)
89 {
90     if (!settings)
91         return;
92 
93     settings->beginGroup("remote_control");
94 
95     // Get port number; restart server if running
96     if (settings->contains("port"))
97         setPort(settings->value("port").toInt());
98 
99     // Get list of allowed hosts
100     if (settings->contains("allowed_hosts"))
101         setHosts(settings->value("allowed_hosts").toStringList());
102 
103     settings->endGroup();
104 }
105 
saveSettings(QSettings * settings) const106 void RemoteControl::saveSettings(QSettings *settings) const
107 {
108     if (!settings)
109         return;
110 
111     settings->beginGroup("remote_control");
112 
113     if (rc_server.isListening())
114         settings->setValue("enabled", true);
115     else
116         settings->remove("enabled");
117 
118     if (rc_port != DEFAULT_RC_PORT)
119         settings->setValue("port", rc_port);
120     else
121         settings->remove("port");
122 
123     if (rc_allowed_hosts.count() > 0)
124         settings->setValue("allowed_hosts", rc_allowed_hosts);
125     else
126         settings->remove("allowed_hosts");
127 
128     settings->endGroup();
129 }
130 
131 /*! \brief Set new network port.
132  *  \param port The new network port.
133  *
134  * If the server is running it will be restarted.
135  *
136  */
setPort(int port)137 void RemoteControl::setPort(int port)
138 {
139     if (port == rc_port)
140         return;
141 
142     rc_port = port;
143     if (rc_server.isListening())
144     {
145         rc_server.close();
146         rc_server.listen(QHostAddress::Any, rc_port);
147     }
148 }
149 
setHosts(QStringList hosts)150 void RemoteControl::setHosts(QStringList hosts)
151 {
152     rc_allowed_hosts = hosts;
153 }
154 
155 
156 /*! \brief Accept a new client connection.
157  *
158  * This slot is called when a client opens a new connection.
159  */
acceptConnection()160 void RemoteControl::acceptConnection()
161 {
162     if (rc_socket)
163     {
164         rc_socket->close();
165         rc_socket->deleteLater();
166     }
167     rc_socket = rc_server.nextPendingConnection();
168 
169     // check if host is allowed
170     QString address = rc_socket->peerAddress().toString();
171     if (rc_allowed_hosts.indexOf(address) == -1)
172     {
173         std::cout << "*** Remote connection attempt from " << address.toStdString()
174                   << " (not in allowed list)" << std::endl;
175         rc_socket->close();
176         rc_socket->deleteLater();
177         rc_socket = 0;
178     }
179     else
180     {
181         connect(rc_socket, SIGNAL(readyRead()), this, SLOT(startRead()));
182     }
183 }
184 
185 /*! \brief Start reading from the socket.
186  *
187  * This slot is called when the client TCP socket emits a readyRead() signal,
188  * i.e. when there is data to read.
189  */
startRead()190 void RemoteControl::startRead()
191 {
192     char    buffer[1024] = {0};
193     int     bytes_read;
194     QString answer = "";
195 
196     bytes_read = rc_socket->readLine(buffer, 1024);
197     if (bytes_read < 2)  // command + '\n'
198         return;
199 
200     QStringList cmdlist = QString(buffer).trimmed().split(" ", QString::SkipEmptyParts);
201 
202     if (cmdlist.size() == 0)
203         return;
204 
205     QString cmd = cmdlist[0];
206     if (cmd == "f")
207         answer = cmd_get_freq();
208     else if (cmd == "F")
209         answer = cmd_set_freq(cmdlist);
210     else if (cmd == "m")
211         answer = cmd_get_mode();
212     else if (cmd == "M")
213         answer = cmd_set_mode(cmdlist);
214     else if (cmd == "l")
215         answer = cmd_get_level(cmdlist);
216     else if (cmd == "L")
217         answer = cmd_set_level(cmdlist);
218     else if (cmd == "u")
219         answer = cmd_get_func(cmdlist);
220     else if (cmd == "U")
221         answer = cmd_set_func(cmdlist);
222     else if (cmd == "v")
223         answer = cmd_get_vfo();
224     else if (cmd == "V")
225         answer = cmd_set_vfo(cmdlist);
226     else if (cmd == "s")
227         answer = cmd_get_split_vfo();
228     else if (cmd == "S")
229         answer = cmd_set_split_vfo();
230     else if (cmd == "p")
231         answer = cmd_get_param(cmdlist);
232     else if (cmd == "_")
233         answer = cmd_get_info();
234     else if (cmd == "AOS")
235         answer = cmd_AOS();
236     else if (cmd == "LOS")
237         answer = cmd_LOS();
238     else if (cmd == "LNB_LO")
239         answer = cmd_lnb_lo(cmdlist);
240     else if (cmd == "\\dump_state")
241         answer = cmd_dump_state();
242     else if (cmd == "q" || cmd == "Q")
243     {
244         // FIXME: for now we assume 'close' command
245         rc_socket->close();
246         rc_socket->deleteLater();
247         rc_socket = 0;
248         return;
249     }
250     else
251     {
252         // print unknown command and respond with an error
253         qWarning() << "Unknown remote command:" << cmdlist;
254         answer = QString("RPRT 1\n");
255     }
256 
257     rc_socket->write(answer.toLatin1());
258 }
259 
260 /*! \brief Slot called when the receiver is tuned to a new frequency.
261  *  \param freq The new frequency in Hz.
262  *
263  * Note that this is the frequency gqrx is receiving on, i.e. the
264  * hardware frequency + the filter offset.
265  */
setNewFrequency(qint64 freq)266 void RemoteControl::setNewFrequency(qint64 freq)
267 {
268     rc_freq = freq;
269 }
270 
271 /*! \brief Slot called when the filter offset is changed. */
setFilterOffset(qint64 freq)272 void RemoteControl::setFilterOffset(qint64 freq)
273 {
274     rc_filter_offset = freq;
275 }
276 
277 /*! \brief Slot called when the LNB LO frequency has changed
278  *  \param freq_mhz new LNB LO frequency in MHz
279  */
setLnbLo(double freq_mhz)280 void RemoteControl::setLnbLo(double freq_mhz)
281 {
282     rc_lnb_lo_mhz = freq_mhz;
283 }
284 
setBandwidth(qint64 bw)285 void RemoteControl::setBandwidth(qint64 bw)
286 {
287     // we want to leave some margin
288     bw_half = (qint64)(0.9f * (bw / 2.f));
289 }
290 
291 /*! \brief Set signal level in dBFS. */
setSignalLevel(float level)292 void RemoteControl::setSignalLevel(float level)
293 {
294     signal_level = level;
295 }
296 
297 /*! \brief Set demodulator (from mainwindow). */
setMode(int mode)298 void RemoteControl::setMode(int mode)
299 {
300     rc_mode = mode;
301 
302     if (rc_mode == 0)
303         audio_recorder_status = false;
304 }
305 
306 /*! \brief Set passband (from mainwindow). */
setPassband(int passband_lo,int passband_hi)307 void RemoteControl::setPassband(int passband_lo, int passband_hi)
308 {
309     rc_passband_lo = passband_lo;
310     rc_passband_hi = passband_hi;
311 }
312 
313 /*! \brief New remote frequency received. */
setNewRemoteFreq(qint64 freq)314 void RemoteControl::setNewRemoteFreq(qint64 freq)
315 {
316     qint64 delta = freq - rc_freq;
317     qint64 bwh_eff = 0.8f * (float)bw_half;
318 
319     rc_filter_offset += delta;
320     if ((rc_filter_offset > 0 && rc_filter_offset + rc_passband_hi < bwh_eff) ||
321         (rc_filter_offset < 0 && rc_filter_offset + rc_passband_lo > -bwh_eff))
322     {
323         // move filter offset
324         emit newFilterOffset(rc_filter_offset);
325     }
326     else
327     {
328         // moving filter offset would push it too close to or beyond the edge
329         // move it close to the center and adjust hardware freq
330         if (rc_filter_offset < 0)
331             rc_filter_offset = -0.2f * bwh_eff;
332         else
333             rc_filter_offset = 0.2f * bwh_eff;
334         emit newFilterOffset(rc_filter_offset);
335         emit newFrequency(freq);
336     }
337 
338     rc_freq = freq;
339 }
340 
341 /*! \brief Set squelch level (from mainwindow). */
setSquelchLevel(double level)342 void RemoteControl::setSquelchLevel(double level)
343 {
344     squelch_level = level;
345 }
346 
347 /*! \brief Start audio recorder (from mainwindow). */
startAudioRecorder(QString unused)348 void RemoteControl::startAudioRecorder(QString unused)
349 {
350     if (rc_mode > 0)
351         audio_recorder_status = true;
352 }
353 
354 /*! \brief Stop audio recorder (from mainwindow). */
stopAudioRecorder()355 void RemoteControl::stopAudioRecorder()
356 {
357     audio_recorder_status = false;
358 }
359 
360 /*! \brief Set receiver status (from mainwindow). */
setReceiverStatus(bool enabled)361 void RemoteControl::setReceiverStatus(bool enabled)
362 {
363     receiver_running = enabled;
364 }
365 
366 /*! \brief Set available gain settings (from mainwindow). */
setGainStages(gain_list_t & gain_list)367 void RemoteControl::setGainStages(gain_list_t &gain_list)
368 {
369     gains = gain_list;
370 }
371 
372 /*! \brief Set value for a specific gain setting (from DockInputCtl). */
setGain(QString name,double gain)373 bool RemoteControl::setGain(QString name, double gain)
374 {
375     for(auto &g : gains)
376     {
377         if(name == QString::fromStdString(g.name))
378         {
379             if(gain != g.value) {
380                 g.value = gain;
381                 emit gainChanged(name, gain);
382             }
383             return true;
384         }
385     }
386     return false;
387 }
388 
389 /*! \brief Set RDS program identification (from RDS parser). */
rdsPI(QString program_id)390 void RemoteControl::rdsPI(QString program_id)
391 {
392     rc_program_id = program_id;
393 }
394 
395 /*! \brief Set RDS status (from RDS dock). */
setRDSstatus(bool enabled)396 void RemoteControl::setRDSstatus(bool enabled)
397 {
398     rds_status = enabled;
399     rc_program_id = "0000";
400 }
401 
402 /*! \brief Convert mode string to enum (DockRxOpt::rxopt_mode_idx)
403  *  \param mode The Hamlib rigctld compatible mode string
404  *  \return An integer corresponding to the mode.
405  *
406  * Following mode strings are recognized: OFF, RAW, AM, AMS, FM, WFM,
407  * WFM_ST, WFM_ST_OIRT, LSB, USB, CW, CWL, CWU.
408  */
modeStrToInt(QString mode_str)409 int RemoteControl::modeStrToInt(QString mode_str)
410 {
411     int mode_int = -1;
412 
413     if (mode_str.compare("OFF", Qt::CaseInsensitive) == 0)
414     {
415         mode_int = DockRxOpt::MODE_OFF;
416     }
417     else if (mode_str.compare("RAW", Qt::CaseInsensitive) == 0)
418     {
419         mode_int = DockRxOpt::MODE_RAW;
420     }
421     else if (mode_str.compare("AM", Qt::CaseInsensitive) == 0)
422     {
423         mode_int = DockRxOpt::MODE_AM;
424     }
425     else if (mode_str.compare("AMS", Qt::CaseInsensitive) == 0)
426     {
427         mode_int = DockRxOpt::MODE_AM_SYNC;
428     }
429     else if (mode_str.compare("LSB", Qt::CaseInsensitive) == 0)
430     {
431         mode_int = DockRxOpt::MODE_LSB;
432     }
433     else if (mode_str.compare("USB", Qt::CaseInsensitive) == 0)
434     {
435         mode_int = DockRxOpt::MODE_USB;
436     }
437     else if (mode_str.compare("CWL", Qt::CaseInsensitive) == 0)
438     {
439         mode_int = DockRxOpt::MODE_CWL;
440         hamlib_compatible = false;
441     }
442     else if (mode_str.compare("CWR", Qt::CaseInsensitive) == 0)  // "CWR" : "CWL"
443     {
444         mode_int = DockRxOpt::MODE_CWL;
445         hamlib_compatible = true;
446     }
447     else if (mode_str.compare("CWU", Qt::CaseInsensitive) == 0)
448     {
449         mode_int = DockRxOpt::MODE_CWU;
450         hamlib_compatible = false;
451     }
452     else if (mode_str.compare("CW", Qt::CaseInsensitive) == 0)  // "CW" : "CWU"
453     {
454         mode_int = DockRxOpt::MODE_CWU;
455         hamlib_compatible = true;
456     }
457     else if (mode_str.compare("FM", Qt::CaseInsensitive) == 0)
458     {
459         mode_int = DockRxOpt::MODE_NFM;
460     }
461     else if (mode_str.compare("WFM", Qt::CaseInsensitive) == 0)
462     {
463         mode_int = DockRxOpt::MODE_WFM_MONO;
464     }
465     else if (mode_str.compare("WFM_ST", Qt::CaseInsensitive) == 0)
466     {
467         mode_int = DockRxOpt::MODE_WFM_STEREO;
468     }
469     else if (mode_str.compare("WFM_ST_OIRT", Qt::CaseInsensitive) == 0)
470     {
471         mode_int = DockRxOpt::MODE_WFM_STEREO_OIRT;
472     }
473     return mode_int;
474 }
475 
476 /*! \brief Convert mode enum to string.
477  *  \param mode The mode ID c.f. DockRxOpt::rxopt_mode_idx
478  *  \returns The mode string.
479  */
intToModeStr(int mode)480 QString RemoteControl::intToModeStr(int mode)
481 {
482     QString mode_str;
483 
484     switch (mode)
485     {
486     case DockRxOpt::MODE_OFF:
487         mode_str = "OFF";
488         break;
489 
490     case DockRxOpt::MODE_RAW:
491         mode_str = "RAW";
492         break;
493 
494     case DockRxOpt::MODE_AM:
495         mode_str = "AM";
496         break;
497 
498     case DockRxOpt::MODE_AM_SYNC:
499         mode_str = "AMS";
500         break;
501 
502     case DockRxOpt::MODE_LSB:
503         mode_str = "LSB";
504         break;
505 
506     case DockRxOpt::MODE_USB:
507         mode_str = "USB";
508         break;
509 
510     case DockRxOpt::MODE_CWL:
511         mode_str = (hamlib_compatible) ? "CWR" : "CWL";
512         break;
513 
514     case DockRxOpt::MODE_CWU:
515         mode_str = (hamlib_compatible) ? "CW" : "CWU";
516         break;
517 
518     case DockRxOpt::MODE_NFM:
519         mode_str = "FM";
520         break;
521 
522     case DockRxOpt::MODE_WFM_MONO:
523         mode_str = "WFM";
524         break;
525 
526     case DockRxOpt::MODE_WFM_STEREO:
527         mode_str = "WFM_ST";
528         break;
529 
530     case DockRxOpt::MODE_WFM_STEREO_OIRT:
531         mode_str = "WFM_ST_OIRT";
532         break;
533 
534     default:
535         mode_str = "ERR";
536         break;
537     }
538 
539     return mode_str;
540 }
541 
542 /* Get frequency */
cmd_get_freq() const543 QString RemoteControl::cmd_get_freq() const
544 {
545     return QString("%1\n").arg(rc_freq);
546 }
547 
548 /* Set new frequency */
cmd_set_freq(QStringList cmdlist)549 QString RemoteControl::cmd_set_freq(QStringList cmdlist)
550 {
551     bool ok;
552     double freq = cmdlist.value(1, "ERR").toDouble(&ok);
553 
554     if (ok)
555     {
556         setNewRemoteFreq((qint64)freq);
557         return QString("RPRT 0\n");
558     }
559 
560     return QString("RPRT 1\n");
561 }
562 
563 /* Get mode and passband */
cmd_get_mode()564 QString RemoteControl::cmd_get_mode()
565 {
566     return QString("%1\n%2\n")
567                    .arg(intToModeStr(rc_mode))
568                    .arg(rc_passband_hi - rc_passband_lo);
569 }
570 
571 /* Set mode and passband */
cmd_set_mode(QStringList cmdlist)572 QString RemoteControl::cmd_set_mode(QStringList cmdlist)
573 {
574     QString answer;
575     QString cmd_arg = cmdlist.value(1, "");
576 
577     if (cmd_arg == "?")
578         answer = QString("OFF RAW AM AMS LSB USB CWL CWR CWU CW FM WFM WFM_ST WFM_ST_OIRT\n");
579     else
580     {
581         int mode = modeStrToInt(cmd_arg);
582         if (mode == -1)
583         {
584             // invalid mode string
585             answer = QString("RPRT 1\n");
586         }
587         else
588         {
589             rc_mode = mode;
590             emit newMode(rc_mode);
591 
592             int passband = cmdlist.value(2, "0").toInt();
593             if ( passband != 0 )
594                 emit newPassband(passband);
595 
596             if (rc_mode == 0)
597                 audio_recorder_status = false;
598 
599             answer = QString("RPRT 0\n");
600         }
601     }
602     return answer;
603 }
604 
605 /* Get level */
cmd_get_level(QStringList cmdlist)606 QString RemoteControl::cmd_get_level(QStringList cmdlist)
607 {
608     QString answer;
609     QString lvl = cmdlist.value(1, "");
610 
611     if (lvl == "?")
612     {
613         QStringList names;
614         for(auto &g : gains)
615             names.push_back(QString("%1_GAIN").arg(QString::fromStdString(g.name)));
616         answer = QString("SQL STRENGTH %1\n").arg(names.join(" "));
617     }
618     else if (lvl.compare("STRENGTH", Qt::CaseInsensitive) == 0 || lvl.isEmpty())
619     {
620         answer = QString("%1\n").arg(signal_level, 0, 'f', 1);
621     }
622     else if (lvl.compare("SQL", Qt::CaseInsensitive) == 0)
623     {
624         answer = QString("%1\n").arg(squelch_level, 0, 'f', 1);
625     }
626     else if (lvl.contains(QRegExp("_GAIN$")))
627     {
628         QString name = lvl.remove(QRegExp("_GAIN$"));
629         answer = QString("RPRT 1\n");
630         for(auto &g : gains)
631         {
632             if(name == QString::fromStdString(g.name))
633             {
634                 answer = QString("%1\n").arg(g.value);
635                 break;
636             }
637         }
638     }
639     else
640     {
641         answer = QString("RPRT 1\n");
642     }
643 
644     return answer;
645 }
646 
647 /* Set level */
cmd_set_level(QStringList cmdlist)648 QString RemoteControl::cmd_set_level(QStringList cmdlist)
649 {
650     QString answer;
651     QString lvl = cmdlist.value(1, "");
652 
653     if (lvl == "?")
654     {
655         QStringList names;
656         for(auto &g : gains)
657             names.push_back(QString("%1_GAIN").arg(QString::fromStdString(g.name)));
658         answer = QString("SQL %1\n").arg(names.join(" "));
659     }
660     else if (lvl.compare("SQL", Qt::CaseInsensitive) == 0)
661     {
662         bool ok;
663         double squelch = cmdlist.value(2, "ERR").toDouble(&ok);
664         if (ok)
665         {
666             answer = QString("RPRT 0\n");
667             squelch_level = std::max<double>(-150, std::min<double>(0, squelch));
668             emit newSquelchLevel(squelch_level);
669         }
670         else
671         {
672             answer = QString("RPRT 1\n");
673         }
674     }
675     else if (lvl.contains(QRegExp("_GAIN$")))
676     {
677         QString name = lvl.remove(QRegExp("_GAIN$"));
678 
679         bool ok;
680         double gain = cmdlist.value(2, "ERR").toDouble(&ok);
681         if (ok && setGain(name, gain))
682             answer = QString("RPRT 0\n");
683         else
684             answer = QString("RPRT 1\n");
685     }
686     else
687     {
688         answer = QString("RPRT 1\n");
689     }
690 
691     return answer;
692 }
693 
694 /* Get function */
cmd_get_func(QStringList cmdlist)695 QString RemoteControl::cmd_get_func(QStringList cmdlist)
696 {
697     QString answer;
698     QString func = cmdlist.value(1, "");
699 
700     if (func == "?")
701         answer = QString("RECORD DSP RDS\n");
702     else if (func.compare("RECORD", Qt::CaseInsensitive) == 0)
703         answer = QString("%1\n").arg(audio_recorder_status);
704     else if (func.compare("DSP", Qt::CaseInsensitive) == 0)
705         answer = QString("%1\n").arg(receiver_running);
706     else if (func.compare("RDS", Qt::CaseInsensitive) == 0)
707         answer = QString("%1\n").arg(rds_status);
708     else
709         answer = QString("RPRT 1\n");
710 
711     return answer;
712 }
713 
714 /* Set function */
cmd_set_func(QStringList cmdlist)715 QString RemoteControl::cmd_set_func(QStringList cmdlist)
716 {
717     bool ok;
718     QString answer;
719     QString func = cmdlist.value(1, "");
720     int     status = cmdlist.value(2, "ERR").toInt(&ok);
721 
722     if (func == "?")
723     {
724         answer = QString("RECORD DSP RDS\n");
725     }
726     else if ((func.compare("RECORD", Qt::CaseInsensitive) == 0) && ok)
727     {
728         if (rc_mode == 0 || !receiver_running)
729         {
730             answer = QString("RPRT 1\n");
731         }
732         else
733         {
734             answer = QString("RPRT 0\n");
735             audio_recorder_status = status;
736             if (status)
737                 emit startAudioRecorderEvent();
738             else
739                 emit stopAudioRecorderEvent();
740         }
741     }
742     else if ((func.compare("DSP", Qt::CaseInsensitive) == 0) && ok)
743     {
744         if (status)
745             emit dspChanged(true);
746         else
747             emit dspChanged(false);
748 
749         answer = QString("RPRT 0\n");
750     }
751     else if ((func.compare("RDS", Qt::CaseInsensitive) == 0) && ok)
752     {
753         if (status)
754             emit newRDSmode(true);
755         else
756             emit newRDSmode(false);
757 
758         answer = QString("RPRT 0\n");
759     }
760     else
761     {
762         answer = QString("RPRT 1\n");
763     }
764 
765     return answer;
766 }
767 
768 /* Get parameter */
cmd_get_param(QStringList cmdlist)769 QString RemoteControl::cmd_get_param(QStringList cmdlist)
770 {
771     QString answer;
772     QString func = cmdlist.value(1, "");
773 
774     if (func == "?")
775         answer = QString("RDS_PI\n");
776     else if (func.compare("RDS_PI", Qt::CaseInsensitive) == 0)
777         answer = QString("%1\n").arg(rc_program_id);
778     else
779         answer = QString("RPRT 1\n");
780 
781     return answer;
782 }
783 
784 /* Get current 'VFO' (fake, only for hamlib) */
cmd_get_vfo() const785 QString RemoteControl::cmd_get_vfo() const
786 {
787     return QString("VFOA\n");
788 };
789 
790 /* Set 'VFO' (fake, only for hamlib) */
cmd_set_vfo(QStringList cmdlist)791 QString RemoteControl::cmd_set_vfo(QStringList cmdlist)
792 {
793     QString cmd_arg = cmdlist.value(1, "");
794     QString answer;
795 
796     if (cmd_arg == "?")
797         answer = QString("VFOA\n");
798     else if (cmd_arg == "VFOA")
799         answer = QString("RPRT 0\n");
800     else
801         answer = QString("RPRT 1\n");
802 
803     return answer;
804 };
805 
806 /* Get 'Split' mode (fake, only for hamlib) */
cmd_get_split_vfo() const807 QString RemoteControl::cmd_get_split_vfo() const
808 {
809     return QString("0\nVFOA\n");
810 };
811 
812 /* Set 'Split' mode (fake, only for hamlib) */
cmd_set_split_vfo()813 QString RemoteControl::cmd_set_split_vfo()
814 {
815     return QString("RPRT 1\n");
816 }
817 
818 /* Get info */
cmd_get_info() const819 QString RemoteControl::cmd_get_info() const
820 {
821     return QString("Gqrx %1\n").arg(VERSION);
822 };
823 
824 /* Gpredict / Gqrx specific command: AOS - satellite AOS event */
cmd_AOS()825 QString RemoteControl::cmd_AOS()
826 {
827     if (rc_mode > 0 && receiver_running)
828     {
829         emit startAudioRecorderEvent();
830         audio_recorder_status = true;
831     }
832     return QString("RPRT 0\n");
833 }
834 
835 /* Gpredict / Gqrx specific command: LOS - satellite LOS event */
cmd_LOS()836 QString RemoteControl::cmd_LOS()
837 {
838     emit stopAudioRecorderEvent();
839     audio_recorder_status = false;
840     return QString("RPRT 0\n");
841 }
842 
843 /* Set the LNB LO value */
cmd_lnb_lo(QStringList cmdlist)844 QString RemoteControl::cmd_lnb_lo(QStringList cmdlist)
845 {
846     if(cmdlist.size() == 2)
847     {
848         bool ok;
849         qint64 freq = cmdlist[1].toLongLong(&ok);
850 
851         if (ok)
852         {
853             rc_lnb_lo_mhz = freq / 1e6;
854             emit newLnbLo(rc_lnb_lo_mhz);
855             return QString("RPRT 0\n");
856         }
857 
858         return QString("RPRT 1\n");
859     }
860     else
861     {
862         return QString("%1\n").arg((qint64)(rc_lnb_lo_mhz * 1e6));
863     }
864 }
865 
866 /*
867  * '\dump_state' used by hamlib clients, e.g. xdx, fldigi, rigctl and etc
868  * More info:
869  *  https://github.com/N0NB/hamlib/blob/master/include/hamlib/rig.h (bit fields)
870  *  https://github.com/N0NB/hamlib/blob/master/dummy/netrigctl.c
871  */
cmd_dump_state() const872 QString RemoteControl::cmd_dump_state() const
873 {
874     return QString(
875         /* rigctl protocol version */
876         "0\n"
877         /* rigctl model */
878         "2\n"
879         /* ITU region */
880         "1\n"
881         /* RX/TX frequency ranges
882          * start, end, modes, low_power, high_power, vfo, ant
883          *  start/end - Start/End frequency [Hz]
884          *  modes - Bit field of RIG_MODE's (AM|AMS|CW|CWR|USB|LSB|FM|WFM)
885          *  low_power/high_power - Lower/Higher RF power in mW,
886          *                         -1 for no power (ie. rx list)
887          *  vfo - VFO list equipped with this range (RIG_VFO_A)
888          *  ant - Antenna list equipped with this range, 0 means all
889          *  FIXME: limits can be gets from receiver::get_rf_range()
890          */
891         "0.000000 10000000000.000000 0x2ef -1 -1 0x1 0x0\n"
892         /* End of RX frequency ranges. */
893         "0 0 0 0 0 0 0\n"
894         /* End of TX frequency ranges. The Gqrx is reciver only. */
895         "0 0 0 0 0 0 0\n"
896         /* Tuning steps: modes, tuning_step */
897         "0xef 1\n"
898         "0xef 0\n"
899         /* End of tuning steps */
900         "0 0\n"
901         /* Filter sizes: modes, width
902          * FIXME: filter can be gets from filter_preset_table
903          */
904         "0x82 500\n"    /* CW | CWR normal */
905         "0x82 200\n"    /* CW | CWR narrow */
906         "0x82 2000\n"   /* CW | CWR wide */
907         "0x221 10000\n" /* AM | AMS | FM normal */
908         "0x221 5000\n"  /* AM | AMS | FM narrow */
909         "0x221 20000\n" /* AM | AMS | FM wide */
910         "0x0c 2700\n"   /* SSB normal */
911         "0x0c 1400\n"   /* SSB narrow */
912         "0x0c 3900\n"   /* SSB wide */
913         "0x40 160000\n" /* WFM normal */
914         "0x40 120000\n" /* WFM narrow */
915         "0x40 200000\n" /* WFM wide */
916         /* End of filter sizes  */
917         "0 0\n"
918         /* max_rit  */
919         "0\n"
920         /* max_xit */
921         "0\n"
922         /* max_ifshift */
923         "0\n"
924         /* Announces (bit field list) */
925         "0\n" /* RIG_ANN_NONE */
926         /* Preamp list in dB, 0 terminated */
927         "0\n"
928         /* Attenuator list in dB, 0 terminated */
929         "0\n"
930         /* Bit field list of get functions */
931         "0\n" /* RIG_FUNC_NONE */
932         /* Bit field list of set functions */
933         "0\n" /* RIG_FUNC_NONE */
934         /* Bit field list of get level */
935         "0x40000020\n" /* RIG_LEVEL_SQL | RIG_LEVEL_STRENGTH */
936         /* Bit field list of set level */
937         "0x20\n"       /* RIG_LEVEL_SQL */
938         /* Bit field list of get parm */
939         "0\n" /* RIG_PARM_NONE */
940         /* Bit field list of set parm */
941         "0\n" /* RIG_PARM_NONE */);
942 }
943