/* -*- Mode: c++ -*- */ /* * Hamlib GNUradio backend - main file * Copyright (c) 2001-2003 by Stephane Fillod * * $Id: gnuradio.cc,v 1.4 2003-04-06 18:50:21 fillods Exp $ * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* * Simple backend of a chirp source. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* FM */ //#include /* AM */ /* SSB mod */ //#include //#include #include #include /* Chirp param's */ #define CARRIER_FREQ 1.070e6 // AM 1070 #define AMPLITUDE 3000 #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include #include #include #include #include #include #include "gnuradio.h" #include "gr_priv.h" // struct gnuradio_priv_data /* * TODO: fft scope, with ext_level to display them */ #define TOK_TUNER_MODEL TOKEN_BACKEND(1) const struct confparams gnuradio_cfg_params[] = { { TOK_TUNER_MODEL, "tuner_model", "Tuner model", "Hamlib rig tuner model number", "1" /* RIG_MODEL_DUMMY */, RIG_CONF_NUMERIC, { /* .n = */ { 0, INT_MAX, 1 } } }, /* * TODO: IF_center_freq, input_rate, etc. */ { RIG_CONF_END, NULL, } }; static void init_chan(RIG *rig, vfo_t vfo, channel_t *chan) { chan->channel_num = 0; chan->vfo = vfo; strcpy(chan->channel_desc, strvfo(vfo)); chan->freq = RIG_FREQ_NONE; chan->mode = RIG_MODE_NONE; chan->width = 0; chan->tx_freq = chan->freq; chan->tx_mode = chan->mode; chan->tx_width = chan->width; chan->split = RIG_SPLIT_OFF; chan->rptr_shift = RIG_RPT_SHIFT_NONE; chan->rptr_offs = 0; chan->ctcss_tone = 0; chan->dcs_code = 0; chan->ctcss_sql = 0; chan->dcs_sql = 0; chan->rit = 0; chan->xit = 0; chan->tuning_step = 0; chan->ant = 0; chan->funcs = (setting_t)0; memset(chan->levels, 0, RIG_SETTING_MAX*sizeof(value_t)); } int gr_init(RIG *rig) { struct gnuradio_priv_data *priv; const struct gnuradio_priv_caps *priv_caps = (const struct gnuradio_priv_caps*)rig->caps->priv; int i; priv = (struct gnuradio_priv_data*)malloc(sizeof(struct gnuradio_priv_data)); if (!priv) return -RIG_ENOMEM; rig->state.priv = (void*)priv; rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__ ); rig->state.rigport.type.rig = RIG_PORT_NONE; memset(priv->parms, 0, RIG_SETTING_MAX*sizeof(value_t)); priv->m = NULL; priv->sink = NULL; priv->source = NULL; priv->need_fixer = 0; for (i=0; ichans[i]); priv->chans[i].levels[rig_setting2idx(RIG_LEVEL_AF)].f = 1.0; priv->chans[i].levels[rig_setting2idx(RIG_LEVEL_RF)].f = 1.0; } priv->curr_vfo = RIG_VFO_A; priv->tuner_model = priv_caps->tuner_model; priv->input_rate = priv_caps->input_rate; priv->IF_center_freq = priv_caps->IF_center_freq; pthread_mutex_init(&priv->mutex_process, NULL); return RIG_OK; } /* * Assumes rig!=NULL, rig->state.priv!=NULL */ int gnuradio_set_conf(RIG *rig, token_t token, const char *val) { struct gnuradio_priv_data *priv; struct rig_state *rs; rs = &rig->state; priv = (struct gnuradio_priv_data*)rs->priv; switch(token) { case TOK_TUNER_MODEL: priv->tuner_model = atoi(val); break; default: /* if it's not for the gnuradio backend, maybe it's for the tuner */ return rig_set_conf(priv->tuner, token, val); } return RIG_OK; } /* * assumes rig!=NULL, * Assumes rig!=NULL, rig->state.priv!=NULL * and val points to a buffer big enough to hold the conf value. */ int gnuradio_get_conf(RIG *rig, token_t token, char *val) { struct gnuradio_priv_data *priv; struct rig_state *rs; rs = &rig->state; priv = (struct gnuradio_priv_data*)rs->priv; switch(token) { case TOK_TUNER_MODEL: sprintf(val, "%d", priv->tuner_model); break; default: /* if it's not for the gnuradio backend, maybe it's for the tuner */ return rig_get_conf(priv->tuner, token, val); } return RIG_OK; } /* * GNUradio process thread * * the thread is _created_ with priv->mutex_process held * * TODO: change process name to give a hint about this CPU hungry process * also shouldn't we block couple of disturbing signals? */ static void *gnuradio_process(void *arg) { RIG *rig = (RIG *)arg; struct gnuradio_priv_data *priv; priv = (struct gnuradio_priv_data*)rig->state.priv; /* the mutex lock is not to gurantee reentrancy of rig_debug, * this is just to know when backend want us start running */ pthread_mutex_lock(&priv->mutex_process); rig_debug(RIG_DEBUG_TRACE,"gnuradio process thread started\n"); pthread_mutex_unlock(&priv->mutex_process); while (priv->do_process) { pthread_mutex_lock(&priv->mutex_process); priv->m->process(); pthread_mutex_unlock(&priv->mutex_process); } return NULL; } int gr_open(RIG *rig) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data*)rig->state.priv; const struct gnuradio_priv_caps *priv_caps = (const struct gnuradio_priv_caps*)rig->caps->priv; int ret; rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__); /* * make sure gnuradio's tuner is not gnuradio! */ if (RIG_BACKEND_NUM(priv_caps->tuner_model) == RIG_GNURADIO) { return -RIG_ECONF; } priv->tuner = rig_init(priv->tuner_model); if (!priv->tuner) { /* FIXME: wrong rig model? */ return -RIG_ENOMEM; } rig_open(priv->tuner); /* TODO: * copy priv->tuner->rx_range/tx_range to rig->state, * and override available modes with gnuradio's */ /* ** Source ** */ // --> short priv->source = new VrFileSource(priv->input_rate, "/tmp/fm95_5_half.dat", true); // Chirp if (!priv->source) priv->source = new VrSigSource(priv->input_rate, VR_SIN_WAVE, CARRIER_FREQ, AMPLITUDE); //new VrChirpSource(priv->input_rate, AMPLITUDE, 4); /* VrFileSource (double sampling_freq, const char *file, bool repeat = false) */ //priv->source = new VrFileSource(priv->input_rate, "microtune_source.sw", true); // short --> short priv->need_fixer = 1; priv->offset_fixer = new VrFixOffset(); NWO_CONNECT (priv->source, priv->offset_fixer); priv->sink = new VrAudioSink(); /* ** Sink ** */ if (!priv->sink) priv->sink = new VrNullSink(); //priv->sink = new VrFileSink("microtune_audio.sw"); priv->m = new VrMultiTask (); priv->m->start(); /* or set it to MODE_NONE? */ //gr_set_mode(rig, RIG_VFO_CURR, RIG_MODE_WFM, RIG_PASSBAND_NORMAL); if (priv->tuner_model == RIG_MODEL_DUMMY) { gr_set_freq(rig, RIG_VFO_CURR, priv->IF_center_freq); } priv->do_process = 1; pthread_mutex_lock(&priv->mutex_process); ret = pthread_create(&priv->process_thread, NULL, gnuradio_process, (void*)rig); pthread_mutex_unlock(&priv->mutex_process); if (ret != 0) { /* TODO: undo the close*/ rig_debug(RIG_DEBUG_ERR, "%s: pthread_create failed: %s\n", __FUNCTION__, strerror(errno)); return -RIG_ENOMEM; /* huh? */ } return RIG_OK; } /* TODO: error checking of new */ int mc4020_open(RIG *rig) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data*)rig->state.priv; /* input sample rate from PCI-DAS4020/12: 20000000 */ priv->source = new GrMC4020Source(priv->input_rate, MCC_CH3_EN | MCC_ALL_1V); return gr_open(rig); } /* * sound card source override */ int graudio_open(RIG *rig) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data*)rig->state.priv; /* * assumes sound card is full duplex! */ priv->source = new VrAudioSource(priv->input_rate); return gr_open(rig); } int gr_close(RIG *rig) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data*)rig->state.priv; int ret; rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__); priv->do_process = 0; pthread_mutex_lock(&priv->mutex_process); priv->m->stop(); pthread_mutex_unlock(&priv->mutex_process); ret = pthread_join(priv->process_thread, NULL); if (ret != 0) { rig_debug(RIG_DEBUG_ERR, "%s: pthread_join failed: %s\n", __FUNCTION__, strerror(errno)); } rig_debug(RIG_DEBUG_TRACE,"%s: process thread stopped\n", __FUNCTION__); delete priv->m; delete priv->sink; delete priv->source; if (priv->need_fixer) delete priv->offset_fixer; priv->m = NULL; priv->sink = NULL; priv->source = NULL; priv->need_fixer = 0; rig_close(priv->tuner); return RIG_OK; } int gr_cleanup(RIG *rig) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data*)rig->state.priv; rig_debug(RIG_DEBUG_VERBOSE,"%s called\n", __FUNCTION__); rig_cleanup(priv->tuner); /* note: mutex must be unlocked! */ pthread_mutex_destroy(&priv->mutex_process); if (rig->state.priv) free(rig->state.priv); rig->state.priv = NULL; return RIG_OK; } static int vfo2chan_num(RIG *rig, vfo_t vfo) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; if (vfo == RIG_VFO_CURR) vfo = priv->curr_vfo; switch (vfo) { case RIG_VFO_A: return 0; case RIG_VFO_B: return 1; } rig_debug(RIG_DEBUG_WARN,"%s unknown vfo: %d\n", vfo); return 0; } /* * tuner_freq is the display freq on tuner of the IF in digital domain * freq is the desired freq */ static int update_freq(RIG *rig, unsigned chan_num, freq_t tuner_freq, freq_t freq) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; channel_t *chan = &priv->chans[chan_num]; struct mod_data *mod = &priv->mods[chan_num]; double freq_offset; /* * In case the tuner is not a real tuner */ if (priv->tuner_model == RIG_MODEL_DUMMY) tuner_freq = priv->IF_center_freq; freq_offset = (double) (freq - tuner_freq); rig_debug(RIG_DEBUG_VERBOSE, "%s: %lld %lld freq_offset=%g\n", __FUNCTION__, tuner_freq, freq, freq_offset); if (freq_offset == 0) return RIG_OK; /* nothing to do */ /* TODO: change mode plumbing (don't forget locking!) * workaround?: set_mode(NONE), set_mode(previous) */ pthread_mutex_lock(&priv->mutex_process); switch (chan->mode) { case RIG_MODE_WFM: case RIG_MODE_FM: case RIG_MODE_USB: /* not so sure about if setCenter_Freq is the Right thing to do(tm). */ mod->chan_filter->setCenterFreq(priv->IF_center_freq + freq_offset); break; /* * set_freq for SSB mod * mod->ssb.shifter->set_freq(2*M_PI*freq_offset/(double)priv->input_rate); */ case RIG_MODE_NONE: break; default: rig_debug(RIG_DEBUG_WARN, "%s: mode %s unimplemented!\n", __FUNCTION__, strrmode(chan->mode)); break; } pthread_mutex_unlock(&priv->mutex_process); return RIG_OK; } /* * rig_set_freq is a good candidate for the GNUradio GUI setFrequency callback? */ int gr_set_freq(RIG *rig, vfo_t vfo, freq_t freq) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; freq_t tuner_freq; int ret, i; char fstr[20]; sprintf_freq(fstr, freq); rig_debug(RIG_DEBUG_TRACE,"%s called: %s %s\n", __FUNCTION__, strvfo(vfo), fstr); ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq); if (ret != RIG_OK) return ret; /* check if we're out of current IF window * TODO: with several VFO's, center the IF inbetween if out of window */ if (freq < tuner_freq || freq /* + mode_width */ > tuner_freq+GR_MAX_FREQUENCY(priv)) { /* * do not set tuner to freq, but center it */ ret = rig_set_freq(priv->tuner, RIG_VFO_CURR, freq + GR_MAX_FREQUENCY(priv)/2); if (ret != RIG_OK) return ret; /* * query freq right back, because wanted freq may not be real freq, * because of resolution of tuner */ ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq); if (ret != RIG_OK) return ret; /* * tuner freq changed, so adjust frequency offset of other channels */ for (i = 0; ichans[i].freq); } } ret = update_freq(rig, chan_num, tuner_freq, freq); if (ret == RIG_OK) chan->freq = freq; return ret; } int gr_get_freq(RIG *rig, vfo_t vfo, freq_t *freq) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n", __FUNCTION__, strvfo(vfo)); *freq = chan->freq; return RIG_OK; } /* * WIP */ int gr_set_mode(RIG *rig, vfo_t vfo, rmode_t mode, pbwidth_t width) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; struct mod_data *mod = &priv->mods[chan_num]; char buf[16]; freq_t tuner_freq; int ret = RIG_OK; double freq_offset; if (width == RIG_PASSBAND_NORMAL) width = rig_passband_normal(rig, mode); sprintf_freq(buf, width); rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s %s %s\n", __FUNCTION__, strvfo(vfo), strrmode(mode), buf); if (mode == chan->mode && width == chan->width) return RIG_OK; /* * TODO: check if only change in width */ ret = rig_get_freq(priv->tuner, RIG_VFO_CURR, &tuner_freq); if (ret != RIG_OK) return ret; /* * In case the tuner is not a real tuner */ if (priv->tuner_model == RIG_MODEL_DUMMY) tuner_freq = 0; freq_offset = (double) (chan->freq - tuner_freq); rig_debug(RIG_DEBUG_VERBOSE, "%s: freq_offset=%g\n", __FUNCTION__, freq_offset); pthread_mutex_lock(&priv->mutex_process); /* TODO: destroy GNUradio connections beforehand if different mode? */ if (chan->mode != RIG_MODE_NONE && mode != chan->mode) { switch (chan->mode) { case RIG_MODE_FM: case RIG_MODE_WFM: delete mod->demod.wfm.demod; delete mod->chan_filter; delete mod->audio_filter; break; case RIG_MODE_USB: delete mod->audio_filter; delete mod->chan_filter; break; case RIG_MODE_AM: delete mod->chan_filter; delete mod->audio_filter; default: break; } } /* TODO: if same mode, but different width, then adapt */ priv->m->stop(); switch (mode) { case RIG_MODE_FM: { mod->CFIRdecimate = 20; mod->CFIRdecimate2 = 25; mod->RFIRdecimate = 5; const float channelSpacing = 80e3; const float demodBW = 3e3; const float TAU = 75e-6; // 75us in US, 50us in EUR const int quadRate = priv->input_rate / mod->CFIRdecimate / mod->CFIRdecimate2; const int audioRate = quadRate / mod->RFIRdecimate; // // design first stage selection filter // // width of transition band. // // We make this twice as wide as you would think. This allows // some aliasing, but it's outside our final bandwidth, determined // by the next filter float transition_width = (priv->input_rate / mod->CFIRdecimate - width); vector cs1_coeffs = gr_firdes::low_pass (1.0, // gain priv->input_rate, // sampling rate width / 2, // low-pass cutoff freq transition_width, gr_firdes::WIN_HANN); cerr << "Number of cs1_coeffs: " << cs1_coeffs.size () << endl; // // design second stage channel selection filter // // This is the one that we tune // vector cs2_coeffs = gr_firdes::low_pass (1.0, priv->input_rate / mod->CFIRdecimate, width / 2, (channelSpacing - width) / 2, gr_firdes::WIN_HANN); cerr << "Number of cs2_coeffs: " << cs2_coeffs.size () << endl; // // design audio filter // vector audio_coeffs = gr_firdes::band_pass (1e3, // gain quadRate, // sampling rate 300, demodBW, 250, gr_firdes::WIN_HAMMING); cerr << "Number of audio_coeffs: " << audio_coeffs.size () << endl; // // design demphasis filter // vector fftaps = vector(2); vector fbtaps = vector(2); fftaps[0] = 1 - exp(-1/(TAU*audioRate)); fftaps[1] = 0; fbtaps[0] = 0; fbtaps[1] = exp(-1/TAU/priv->input_rate*50);; // // now instantiate the modules // // short --> VrComplex mod->chan_filter = new GrFreqXlatingFIRfilterSCF (mod->CFIRdecimate, cs1_coeffs, priv->IF_center_freq - freq_offset); // VrComplex --> VrComplex mod->demod.fm.chan2_filter = new GrFreqXlatingFIRfilterCCF (mod->CFIRdecimate2, cs2_coeffs, 0); // VrComplex --> float mod->demod.fm.demod = new VrQuadratureDemod(1); // float --> float mod->audioF_filter = new GrFIRfilterFFF (mod->RFIRdecimate, audio_coeffs); // float --> float mod->demod.fm.deemph = new GrIIRfilter (1,fftaps,fbtaps); // float --> short mod->demod.fm.cfs = new GrConvertFS (); //connect the modules together NWO_CONNECT (GR_SOURCE(priv), mod->chan_filter); NWO_CONNECT (mod->chan_filter, mod->demod.fm.chan2_filter); NWO_CONNECT (mod->demod.fm.chan2_filter, mod->demod.fm.demod); NWO_CONNECT (mod->demod.fm.demod, mod->audioF_filter); NWO_CONNECT (mod->audioF_filter, mod->demod.fm.deemph); NWO_CONNECT (mod->demod.fm.deemph, mod->demod.fm.cfs); NWO_CONNECT (mod->demod.fm.cfs, priv->sink); break; } case RIG_MODE_WFM: { mod->CFIRdecimate = 125; mod->RFIRdecimate = 5; const int quadRate = priv->input_rate / mod->CFIRdecimate; const int audioRate = quadRate / mod->RFIRdecimate; rig_debug(RIG_DEBUG_VERBOSE, "Input Sampling Rate: %d\n", priv->input_rate); rig_debug(RIG_DEBUG_VERBOSE, "Complex FIR decimation factor: %d\n", mod->CFIRdecimate); rig_debug(RIG_DEBUG_VERBOSE, "QuadDemod Sampling Rate: %d\n", quadRate); rig_debug(RIG_DEBUG_VERBOSE, "Real FIR decimation factor: %d\n", mod->RFIRdecimate); rig_debug(RIG_DEBUG_VERBOSE, "Audio Sampling Rate: %d\n", audioRate); // build channel filter // // note that the totally bogus transition width is because // we don't have enough mips right now to really do the right thing. // This results in a filter with 83 taps, which is just a few // more than the original 75 in microtune_fm_demo. vector channel_coeffs = gr_firdes::low_pass (1.0, // gain priv->input_rate, // sampling rate width, // low-pass cutoff: freq 250e3 8*100e3, // width of transition band gr_firdes::WIN_HAMMING); rig_debug(RIG_DEBUG_VERBOSE, "Number of channel_coeffs: %d\n", channel_coeffs.size ()); // short --> VrComplex mod->chan_filter = new GrFreqXlatingFIRfilterSCF(mod->CFIRdecimate, channel_coeffs, priv->IF_center_freq - freq_offset); // float --> short double width_of_transition_band = audioRate / 32; vector audio_coeffs = gr_firdes::low_pass (1.0, // gain quadRate, // sampling rate audioRate/2 - width_of_transition_band, // low-pass cutoff freq width_of_transition_band, gr_firdes::WIN_HAMMING); rig_debug(RIG_DEBUG_VERBOSE, "Number of audio_coeffs: %d\n", audio_coeffs.size ()); rig_debug(RIG_DEBUG_VERBOSE, "Low-pass cutoff freq: %d\n", audioRate/2 - (int)width_of_transition_band); mod->audio_filter = new GrFIRfilterFSF(mod->RFIRdecimate, audio_coeffs); mod->demod.wfm.FMdemodGain = 2200; float volume = chan->levels[rig_setting2idx(RIG_LEVEL_AF)].f; // // setup Wide FM demodulator chain // // VrComplex --> float mod->demod.wfm.demod = new VrQuadratureDemod(volume * mod->demod.wfm.FMdemodGain); //connect the modules together NWO_CONNECT (GR_SOURCE(priv), mod->chan_filter); NWO_CONNECT (mod->chan_filter, mod->demod.wfm.demod); NWO_CONNECT (mod->demod.wfm.demod, mod->audio_filter); NWO_CONNECT (mod->audio_filter, priv->sink); break; } case RIG_MODE_USB: { float rf_gain = chan->levels[rig_setting2idx(RIG_LEVEL_RF)].f; #if 0 //SSB mod: mod->ssb.hilb = new GrHilbert(31); /* what's that 31? */ mod->ssb.shifter = new GrSSBMod(2*M_PI*freq_offset/(double)priv->input_rate, rf_gain); #endif //connect the modules together NWO_CONNECT (GR_SOURCE(priv), mod->chan_filter); NWO_CONNECT (mod->chan_filter, mod->demod.fm.demod); NWO_CONNECT (mod->demod.fm.demod, mod->audio_filter); NWO_CONNECT (mod->audio_filter, priv->sink); break; } #if 0 case RIG_MODE_AM: { float volume = chan->levels[rig_setting2idx(RIG_LEVEL_AF)].f; // // setup Wide FM demodulator chain // // VrComplex --> float mod->am.demod = new VrAmplitudeDemod(0.0, 0.05); //connect the modules together NWO_CONNECT (GR_SOURCE(priv), mod->chan_filter); NWO_CONNECT (mod->chan_filter, mod->am.demod); NWO_CONNECT (mod->am.demod, mod->audio_filter); NWO_CONNECT (mod->audio_filter, priv->sink); break; } #endif case RIG_MODE_NONE: /* ez */ break; default: ret = -RIG_EINVAL; } priv->m->add (priv->sink); priv->m->start(); pthread_mutex_unlock(&priv->mutex_process); if (ret == RIG_OK) { chan->mode = mode; chan->width = width; } return ret; } int gr_get_mode(RIG *rig, vfo_t vfo, rmode_t *mode, pbwidth_t *width) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n", __FUNCTION__, strvfo(vfo)); *mode = chan->mode; *width = chan->width; return RIG_OK; } int gr_set_vfo(RIG *rig, vfo_t vfo) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; if (vfo == RIG_VFO_VFO || vfo == RIG_VFO_RX) return RIG_OK; if (vfo != RIG_VFO_A && vfo != RIG_VFO_B) return -RIG_EINVAL; priv->curr_vfo = vfo; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n", __FUNCTION__, strvfo(vfo)); return RIG_OK; } int gr_get_vfo(RIG *rig, vfo_t *vfo) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; *vfo = priv->curr_vfo; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n", __FUNCTION__, strvfo(*vfo)); return RIG_OK; } static int set_rf_gain(RIG *rig, channel_t *chan, struct mod_data *mod, float gain) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; pthread_mutex_lock(&priv->mutex_process); switch (chan->mode) { case RIG_MODE_USB: { //mod->ssb.shifter->set_gain(gain); break; } case RIG_MODE_NONE: break; case RIG_MODE_WFM: case RIG_MODE_FM: /* chan_filter/VrComplexFIRfilter is missing a setGain method! */ default: rig_debug(RIG_DEBUG_WARN, "%s: mode %s unimplemented!\n", __FUNCTION__, strrmode(chan->mode)); break; } pthread_mutex_unlock(&priv->mutex_process); return RIG_OK; } /* * TODO: * restart machinery after level change (e.g. volume, etc.) */ int gnuradio_set_level(RIG *rig, vfo_t vfo, setting_t level, value_t val) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; struct mod_data *mod = &priv->mods[chan_num]; char lstr[32]; int idx; int ret = RIG_OK; idx = rig_setting2idx(level); if (idx < RIG_SETTING_MAX) chan->levels[idx] = val; if (RIG_LEVEL_IS_FLOAT(level)) sprintf(lstr, "%f", val.f); else sprintf(lstr, "%d", val.i); rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s %s\n",__FUNCTION__, strlevel(level), lstr); /* TODO: check val is in range */ switch (level) { case RIG_LEVEL_RF: ret = set_rf_gain(rig, chan, mod, val.f); break; default: rig_debug(RIG_DEBUG_WARN, "%s: level %s unimplemented!\n", __FUNCTION__, strlevel(level)); break; } return ret; } int gnuradio_get_level(RIG *rig, vfo_t vfo, setting_t level, value_t *val) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; int idx; idx = rig_setting2idx(level); if (idx < RIG_SETTING_MAX) *val = chan->levels[idx]; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n",__FUNCTION__, strlevel(level)); return RIG_OK; } /* * TODO: change BFO... */ int gnuradio_set_rit(RIG *rig, vfo_t vfo, shortfreq_t rit) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); chan->rit = rit; return RIG_OK; } int gnuradio_get_rit(RIG *rig, vfo_t vfo, shortfreq_t *rit) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; *rit = chan->rit; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); return RIG_OK; } /* * nothing much to be done but remembering */ int gnuradio_set_ts(RIG *rig, vfo_t vfo, shortfreq_t ts) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); chan->tuning_step = ts; return RIG_OK; } int gnuradio_get_ts(RIG *rig, vfo_t vfo, shortfreq_t *ts) { struct gnuradio_priv_data *priv = (struct gnuradio_priv_data *)rig->state.priv; int chan_num = vfo2chan_num(rig, vfo); channel_t *chan = &priv->chans[chan_num]; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); *ts = chan->tuning_step; return RIG_OK; } int gnuradio_vfo_op(RIG *rig, vfo_t vfo, vfo_op_t op) { freq_t freq; shortfreq_t ts; int ret = RIG_OK; rig_debug(RIG_DEBUG_VERBOSE,"%s called: %s\n",__FUNCTION__, strvfop(op)); switch (op) { case RIG_OP_UP: ret = gr_get_freq(rig, vfo, &freq); if (ret != RIG_OK) break; ret = gnuradio_get_ts(rig, vfo, &ts); if (ret != RIG_OK) break; ret = gr_set_freq(rig, vfo, freq+ts); /* up */ break; case RIG_OP_DOWN: ret = gr_get_freq(rig, vfo, &freq); if (ret != RIG_OK) break; ret = gnuradio_get_ts(rig, vfo, &ts); if (ret != RIG_OK) break; ret = gr_set_freq(rig, vfo, freq-ts); /* down */ break; default: break; } return ret; } int initrigs_gnuradio(void *be_handle) { rig_debug(RIG_DEBUG_VERBOSE, "gnuradio: _init called\n"); rig_register(&gr_caps); rig_register(&mc4020_caps); rig_register(&graudio_caps); return RIG_OK; } #if 0 VrGUI *guimain = 0; VrGUILayout *horiz = 0; VrGUILayout *vert = 0; if (use_gui_p){ guimain = new VrGUI(argc, argv); horiz = guimain->top->horizontal(); vert = horiz->vertical(); } VrSink *fft_sink1 = 0; VrSink *fft_sink2 = 0; VrSink *fft_sink3 = 0; if (use_gui_p){ // sink1 is channel filter output fft_sink1 = new GrFFTSink(vert, 50, 130, 512); // sink2 is fm demod output fft_sink2 = new GrFFTSink(vert, 40, 140, 512); // sink3 is audio output fft_sink3 = new GrFFTSink(horiz, 40, 140, 512); } if (use_gui_p) NWO_CONNECT (chan_filter, fft_sink1); if (use_gui_p) NWO_CONNECT (demod, fft_sink2); if (use_gui_p) NWO_CONNECT (audio_filter, fft_sink3); VrMultiTask *m = new VrMultiTask (); if (use_gui_p){ m->add (fft_sink1); m->add (fft_sink3); m->add (fft_sink2); } m->add (final_sink); if (use_gui_p) guimain->start (); while (1){ if (use_gui_p) guimain->processEvents(10 /*ms*/); #endif