BOOLEAN SpotTone (SpotToneGen st) { int i, n = st->size; ComplexOSC (st->osc.gen); for (i = 0; i < n; i++) { // in an envelope stage? if (st->stage == SpotTone_RISE) { // still going? if (st->rise.have++ < st->rise.want) { st->curr += st->rise.incr; st->mul = (REAL) (st->scl * sin (st->curr * M_PI / 2.0)); } else { // no, assert steady-state, force level st->curr = 1.0; st->mul = st->scl; st->stage = SpotTone_STDY; // won't come back into envelopes // until FALL asserted from outside } } else if (st->stage == SpotTone_FALL) { // still going? if (st->fall.have++ < st->fall.want) { st->curr -= st->fall.incr; st->mul = (REAL) (st->scl * sin (st->curr * M_PI / 2.0)); } else { // no, assert trailing, force level st->curr = 0.0; st->mul = 0.0; st->stage = SpotTone_HOLD; // won't come back into envelopes hereafter } } // apply envelope // (same base as osc.gen internal buf) CXBdata (st->buf, i) = Cscl (CXBdata (st->buf, i), st->mul); } // indicate whether it's turned itself off // sometime during this pass return st->stage != SpotTone_HOLD; }
void AMDemod (AMD am) { int i; REAL demout; switch (am->mode) { case SAMdet: for (i = 0; i < am->size; i++) { pll (am, CXBdata (am->ibuf, i)); demout = dem (am); CXBdata (am->obuf, i) = Cmplx (demout, demout); } break; case AMdet: for (i = 0; i < am->size; i++) { am->lock.curr = Cmag (CXBdata (am->ibuf, i)); am->dc = 0.9999f * am->dc + 0.0001f * am->lock.curr; am->smooth = 0.5f * am->smooth + 0.5f * (am->lock.curr /*- am->dc*/); /* demout = am->smooth; */ CXBdata (am->obuf, i) = Cmplx (am->smooth, am->smooth); } break; } }
void correctIQ (CXB sigbuf, IQ iq, BOOLEAN isTX, int subchan) { int i; REAL doit; if (IQdoit == 0) return; if (subchan == 0) doit = iq->mu; else doit = 0; if(!isTX) { // if (subchan == 0) // removed so that sub rx's will get IQ correction for (i = 0; i < CXBhave (sigbuf); i++) { iq->del[iq->index] = CXBdata(sigbuf,i); iq->y[iq->index] = Cadd(iq->del[iq->index],Cmul(iq->w[0],Conjg(iq->del[iq->index]))); iq->y[iq->index] = Cadd(iq->y[iq->index],Cmul(iq->w[1],Conjg(iq->y[iq->index]))); iq->w[1] = Csub(iq->w[1], Cscl(Cmul(iq->y[iq->index],iq->y[iq->index]), doit)); // this is where the adaption happens CXBdata(sigbuf,i)=iq->y[iq->index]; iq->index = (iq->index+iq->MASK)&iq->MASK; } //fprintf(stderr, "w1 real: %g, w1 imag: %g\n", iq->w[1].re, iq->w[1].im); fflush(stderr); } else { for (i = 0; i < CXBhave (sigbuf); i++) { CXBimag (sigbuf, i) += iq->phase * CXBreal (sigbuf, i); CXBreal (sigbuf, i) *= iq->gain; } } }
// snapshot -> frequency domain void compute_spectrum (SpecBlock * sb) { int i, j, half = sb->size / 2; // assume timebuf has windowed current snapshot fftwf_execute (sb->plan); if (sb->scale == SPEC_MAG) { for (i = 0, j = half; i < half; i++, j++) { sb->output[i] = (float) Cmag (CXBdata (sb->freqbuf, j)); sb->output[j] = (float) Cmag (CXBdata (sb->freqbuf, i)); } } else { // SPEC_PWR for (i = 0, j = half; i < half; i++, j++) { sb->output[i] = (float) (10.0 * log10 (Csqrmag (CXBdata (sb->freqbuf, j)) + 1e-60)); sb->output[j] = (float) (10.0 * log10 (Csqrmag (CXBdata (sb->freqbuf, i)) + 1e-60)); } } }
BOOLEAN CWTone (CWToneGen cwt) { int i, n = cwt->size; ComplexOSC (cwt->osc.gen); for (i = 0; i < n; i++) { // in an envelope stage? if (cwt->stage == CWTone_RISE) { // still going? if (cwt->rise.have++ < cwt->rise.want) { cwt->curr += cwt->rise.incr; cwt->mul = cwt->scl * (REAL) CWSIN (cwt->curr * M_PI / 2.0); } else { // no, assert steady-state, force level cwt->curr = 1.0; cwt->mul = cwt->scl; cwt->stage = CWTone_STDY; // won't come back into envelopes // until FALL asserted from outside } } else if (cwt->stage == CWTone_FALL) { // still going? if (cwt->fall.have++ < cwt->fall.want) { cwt->curr -= cwt->fall.incr; cwt->mul = cwt->scl * (REAL) CWSIN (cwt->curr * M_PI / 2.0); } else { // no, assert trailing, force level cwt->curr = 0.0; cwt->mul = 0.0; cwt->stage = CWTone_HOLD; // won't come back into envelopes hereafter } } // apply envelope // (same base as osc.gen internal buf) CXBdata (cwt->buf, i) = Cscl (CXBdata (cwt->buf, i), cwt->mul); } CXBhave(cwt->buf) = n; /* kd5tfd added - set have field of buf so correctIQ works */ // indicate whether it's turned itself off // sometime during this pass return cwt->stage != CWTone_HOLD; }
void CSSBDemod::demodulate() { if (m_ibuf == m_obuf) return; unsigned int n = CXBhave(m_ibuf); for (unsigned int i = 0; i < n; i++) CXBdata(m_obuf, i) = CXBdata(m_ibuf, i); CXBhave(m_obuf) = n; }
void SDROMnoiseblanker (NB nb) { int i; for (i = 0; i < CXBsize (nb->sigbuf); i++) { REAL cmag = Cmag (CXBdata (nb->sigbuf, i)); nb->average_sig = Cadd (Cscl (nb->average_sig, 0.75), Cscl (CXBdata (nb->sigbuf, i), 0.25)); nb->average_mag = (REAL) (0.999 * (nb->average_mag) + 0.001 * cmag); if (cmag > (nb->threshold * nb->average_mag)) CXBdata (nb->sigbuf, i) = nb->average_sig; } }
void compute_complex_spectrum(SpecBlock * sb) { int i, j, half = sb->size / 2; // assume timebuf has windowed current snapshot fftwf_execute (sb->plan); for (i = 0, j = half; i < half; i++, j++) { sb->coutput[i] = CXBdata (sb->freqbuf, j); sb->coutput[j] = CXBdata (sb->freqbuf, i); } }
void WSCompand (WSCompander wsc) { int i, n = CXBsize (wsc->buff); //if (wsc->fac != 0.0) { for (i = 0; i < n ; i++) { COMPLEX val = CXBdata (wsc->buff, i); REAL mag = Cmag (val), scl = WSCLookup (wsc, mag); CXBdata (wsc->buff, i) = Cscl (val,scl); } } }
void snap_scope (SpecBlock * sb, int label) { int i, j; // where most recent signal started j = sb->fill; // copy starting from there in circular fashion for (i = 0; i < sb->size; i++) { CXBdata (sb->timebuf, i) = CXBdata (sb->accum, j); j = (++j & sb->mask); } sb->label = label; }
void blms_adapt (BLMS blms) { int sigsize = CXBhave (blms->signal); int sigidx = 0; // fputs("Inside\n",stderr),fflush(stderr); do { int j; memcpy (blms->delay_line, &blms->delay_line[128], sizeof (COMPLEX) * 128); // do overlap move memcpy (&blms->delay_line[128], &CXBdata (blms->signal, sigidx), sizeof (COMPLEX) * 128); // copy in new data fftwf_execute (blms->Xplan); // compute transform of input data for (j = 0; j < 256; j++) { blms->Yhat[j] = Cmul (blms->What[j], blms->Xhat[j]); // Filter new signal in freq. domain blms->Xhat[j] = Conjg (blms->Xhat[j]); // take input data's complex conjugate } fftwf_execute (blms->Yplan); //compute output signal transform for (j = 128; j < 256; j++) blms->y[j] = Cscl (blms->y[j], BLKSCL); memset (blms->y, 0, 128 * sizeof (COMPLEX)); for (j = 128; j < 256; j++) blms->error[j] = Csub (blms->delay_line[j], blms->y[j]); // compute error signal if (blms->filter_type) memcpy (&CXBdata (blms->signal, sigidx), &blms->y[128], 128 * sizeof (COMPLEX)); // if noise filter, output y else memcpy (&CXBdata (blms->signal, sigidx), &blms->error[128], 128 * sizeof (COMPLEX)); // if notch filter, output error fftwf_execute (blms->Errhatplan); // compute transform of the error signal for (j = 0; j < 256; j++) blms->Errhat[j] = Cmul (blms->Errhat[j], blms->Xhat[j]); // compute cross correlation transform fftwf_execute (blms->UPDplan); // compute inverse transform of cross correlation transform for (j = 0; j < 128; j++) blms->update[j] = Cscl (blms->update[j], BLKSCL); memset (&blms->update[128], 0, sizeof (COMPLEX) * 128); // zero the last block of the update, so we get // filter coefficients only at front of buffer fftwf_execute (blms->Wplan); for (j = 0; j < 256; j++) { blms->What[j] = Cadd (Cscl (blms->What[j], blms->leak_rate), // leak the W away Cscl (blms->Update[j], blms->adaptation_rate)); // update at adaptation rate } sigidx += 128; // move to next block in the signal buffer } while (sigidx < sigsize); // done? }
/* ---------------------------------------------------------------------------- */ void FMDemod(FMD fm) { int i; for (i = 0; i < CXBhave(fm->ibuf); i++) { pll(fm, CXBdata(fm->ibuf, i)); fm->afc = (REAL) (0.9999 * fm->afc + 0.0001 * fm->pll.freq.f); CXBreal(fm->obuf, i) = CXBimag(fm->obuf, i) = (fm->pll.freq.f - fm->afc) * fm->cvt; } }
// snapshot of current signal void snap_spectrum (SpecBlock * sb, int label) { int i, j; // where most recent signal started j = sb->fill; // copy starting from there in circular fashion, // applying window as we go if (!sb->polyphase) { for (i = 0; i < sb->size; i++) { CXBdata (sb->timebuf, i) = Cscl (CXBdata (sb->accum, j), sb->window[i]); j = (++j & sb->mask); } } else { int k; for (i = 0; i < sb->size; i++) { CXBreal (sb->timebuf, i) = CXBreal (sb->accum, j) * sb->window[i]; CXBimag (sb->timebuf, i) = CXBimag (sb->accum, j) * sb->window[i]; for (k = 1; k < 8; k++) { int accumidx = (j + k * sb->size) & sb->mask; int winidx = i + k * sb->size; CXBreal (sb->timebuf, i) += CXBreal (sb->accum, accumidx) * sb->window[winidx]; CXBimag (sb->timebuf, i) += CXBimag (sb->accum, accumidx) * sb->window[winidx]; } j = (++j & sb->mask); } } sb->label = label; }
void noiseblanker (NB nb) { int i; for (i = 0; i < CXBsize (nb->sigbuf); i++) { REAL cmag = Cmag (CXBdata (nb->sigbuf, i)); nb->delay[nb->sigindex] = CXBdata (nb->sigbuf, i); nb->average_mag = (REAL) (0.999 * (nb->average_mag) + 0.001 * cmag); if ((nb->hangtime == 0) && (cmag > (nb->threshold * nb->average_mag))) nb->hangtime = 7; if (nb->hangtime > 0) { CXBdata (nb->sigbuf, i) = Cmplx (0.0, 0.0); nb->hangtime--; } else CXBdata (nb->sigbuf, i) = nb->delay[nb->delayindex]; nb->sigindex = (nb->sigindex + 7) & 7; nb->delayindex = (nb->delayindex + 7) & 7; } }
void CFMDemod::demodulate() { unsigned int n = CXBhave(m_ibuf); for (unsigned int i = 0; i < n; i++) { pll(CXBdata(m_ibuf, i)); m_afc = float(0.9999 * m_afc + 0.0001F * m_pllFreqF); CXBreal(m_obuf, i) = CXBimag(m_obuf, i) = (m_pllFreqF - m_afc) * m_cvt; } CXBhave(m_obuf) = n; }
void CDCBlock::block() { unsigned int n = CXBhave(m_buf); for (unsigned int i = 0; i < n; i++) { float x = CXBreal(m_buf, i); float y = x - m_xm1 + 0.995F * m_ym1; m_xm1 = x; m_ym1 = y; CXBdata(m_buf, i) = Cmplx(y, 0.0F); } }
static void lmsr_adapt_i (LMSR lms) { int i, j, k; REAL sum_sq, scl1, scl2; COMPLEX accum, error; scl1 = (REAL) (1.0 - rate * leak); for (i = 0; i < ssiz; i++) { dlay (dptr) = CXBdata (lms->signal, i); accum = cxzero; sum_sq = 0; for (j = 0; j < asiz; j+=2) { k = wrap (j); sum_sq += Csqrmag (dlay (k)); accum.re += afil (j).re * dlay (k).re; accum.im += afil (j).im * dlay (k).im; k = wrap (j+1); sum_sq += Csqrmag (dlay (k)); accum.re += afil (j+1).re * dlay (k).re; accum.im += afil (j+1).im * dlay (k).im; } error = Csub(cssig(i),accum); cssig(i) = error; // ssig_i (i) = error.im; // ssig (i) = error.re; scl2 = (REAL) (rate / (sum_sq + 1.19e-7)); error = Cscl(error,scl2); for (j = 0; j < asiz; j+=2) { k = wrap (j); afil (j).re = afil (j).re * scl1 + error.re * dlay (k).re; afil (j).im = afil (j).im * scl1 + error.im * dlay (k).im; k = wrap (j+1); afil (j+1).re = afil (j+1).re * scl1 + error.re * dlay (k).re; afil (j+1).im = afil (j+1).im * scl1 + error.im * dlay (k).im; } dptr = bump (dptr); } }
void DttSPAgc (DTTSPAGC a, int tick) { int i; int hangtime = (int) (uni[0].samplerate * a->hangtime); int fasthangtime = (int) (uni[0].samplerate * a->fasthangtime); REAL hangthresh; if (a->hangthresh > 0) hangthresh = a->gain.top * a->hangthresh + a->gain.bottom * (REAL) (1.0 - a->hangthresh); else hangthresh = 0.; if (a->mode == 0) { for (i = 0; i < CXBsize (a->buff); i++) CXBdata (a->buff, i) = Cscl (CXBdata (a->buff, i), a->gain.fix); return; } for (i = 0; i < CXBsize (a->buff); i++) { REAL tmp; a->circ[a->indx] = CXBdata (a->buff, i); /* Drop sample into circular buffer */ tmp = Cmag (a->circ[a->indx]); if (tmp > 0.00000005f) tmp = a->gain.limit / tmp; // if not zero sample, calculate gain else tmp = a->gain.now; // update. If zero, then use old gain if (tmp < hangthresh) a->hangindex = hangtime; if (tmp >= a->gain.now) { //a->gain.raw = a->one_m_decay * a->gain.now + a->decay * tmp; if (a->hangindex++ > hangtime) { a->gain.now = a->one_m_decay * a->gain.now + a->decay * min (a->gain.top, tmp); } } else { a->hangindex = 0; //a->gain.raw = a->one_m_attack * a->gain.now + a->attack * tmp; a->gain.now = a->one_m_attack * a->gain.now + a->attack * max (tmp, a->gain.bottom); } tmp = Cmag (a->circ[a->fastindx]); if (tmp > 0.00000005f) tmp = a->gain.limit / tmp; else tmp = a->gain.fastnow; if (tmp > a->gain.fastnow) { if (a->fasthang++ > fasthangtime) { a->gain.fastnow = min (a->one_m_fastdecay * a->gain.fastnow + a->fastdecay * min (a->gain.top, tmp), a->gain.top); } } else { a->fasthang = 0; a->gain.fastnow = max (a->one_m_fastattack * a->gain.fastnow + a->fastattack * max (tmp, a->gain.bottom), a->gain.bottom); } a->gain.fastnow = max (min (a->gain.fastnow, a->gain.top), a->gain.bottom); a->gain.now = max (min (a->gain.now, a->gain.top), a->gain.bottom); CXBdata (a->buff, i) = Cscl (a->circ[a->sndx], min (a->gain.fastnow, min (a->slope * a->gain.now, a->gain.top))); a->indx = (a->indx + a->mask) & a->mask; a->sndx = (a->sndx + a->mask) & a->mask; a->fastindx = (a->fastindx + a->mask) & a->mask; } }
void DttSPAgc (DTTSPAGC a, int tick) { int i; int hangtime = (int) (uni[0].samplerate * a->hangtime); // hangtime in samples int fasthangtime = (int) (uni[0].samplerate * a->fasthangtime); // fast track hangtime in samples REAL hangthresh; // gate for whether to hang or not // if (a->hangthresh > 0) hangthresh = a->gain.top * a->hangthresh + a->gain.bottom * (REAL) (1.0 - a->hangthresh); // else // hangthresh = 0.; if (a->mode == 0) { for (i = 0; i < CXBsize (a->buff); i++) CXBdata (a->buff, i) = Cscl (CXBdata (a->buff, i), a->gain.fix); return; } for (i = 0; i < CXBsize (a->buff); i++) { REAL tmp; a->circ[a->slowindx] = CXBdata (a->buff, i); /* Drop sample into circular buffer */ // first, calculate slow gain tmp = Cmag (a->circ[a->slowindx]); if (tmp > 0.0f) tmp = a->gain.target / tmp; // if mag(sample) not too small, calculate putative gain // all the rest of the code which follows is running this // signal through the control laws. else tmp = a->gain.now; // sample too small, just use old gain if (tmp < hangthresh) a->hangindex = hangtime; // If the gain is less than the current hang threshold, then stop hanging. if (tmp >= a->gain.now) // If the putative gain is greater than the current gain then we are in decay. // That is, we need to "decay the ALC voltage", or increase the gain. { //a->gain.raw = a->one_m_decay * a->gain.now + a->decay * tmp; if (a->hangindex++ > hangtime) // Have we HUNG around long enough? { a->gain.now = // Compute the applicable slow channel gain through the decay control law. a->one_m_decay * a->gain.now + a->decay * min (a->gain.top, tmp); } } else // if the putative gain is greater than the current gain, we are in attack mode and need to decrease gain { a->hangindex = 0; // We don't need to hang, we need to attack so we reset the hang index to zero //a->gain.raw = a->one_m_attack * a->gain.now + a->attack * tmp; a->gain.now = // Compute the applicable slow channel gain through the attack control law. a->one_m_attack * a->gain.now + a->attack * max (tmp, a->gain.bottom); } // then, calculate fast gain // Fast channel to handle short duration events and not capture the slow channel decay tmp = Cmag (a->circ[a->fastindx]); if (tmp > 0.0f) tmp = a->gain.target / tmp; // if mag(sample) not too small, calculate putative gain // all the rest of the code which follows is running this // signal through the control laws. else tmp = a->gain.fastnow; // too small, just use old gain if (tmp > a->gain.fastnow) // If the putative gain is greater than the current gain then we are in decay. // That is, we need to "decay the ALC voltage", or increase the gain. { if (a->fasthang++ > fasthangtime) // Have we HUNG around long enough? { a->gain.fastnow = // Compute the applicable fast channel gain through the decay control law. a->one_m_fastdecay * a->gain.fastnow + a->fastdecay * min (a->gain.top, tmp); } } else { a->fasthang = 0; // We don't need to hang, we need to attack so we reset the hang index to zero a->gain.fastnow = // Compute the applicable fast channel gain through the fast attack control law. a->one_m_fastattack * a->gain.fastnow + a->fastattack * max (tmp, a->gain.bottom); } // Are these two lines necessary? I don't think so. Let's test that. tmp is bounded in the statements // above to be inside the gain limits // a->gain.fastnow = // max (min (a->gain.fastnow, a->gain.top), a->gain.bottom); // a->gain.now = max (min (a->gain.now, a->gain.top), a->gain.bottom); // Always apply the lower gain. CXBdata (a->buff, i) = Cscl (a->circ[a->out_indx], min (a->gain.fastnow, (a->slope * a->gain.now))); // Move the indices to prepare for the next sample to be processed a->slowindx = (a->slowindx + a->mask) & a->mask; a->out_indx = (a->out_indx + a->mask) & a->mask; a->fastindx = (a->fastindx + a->mask) & a->mask; } }