Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 83e7494d7ddb
Choose a base ref
...
head repository: ngscopeclient/scopehal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 31a7e4204e83
Choose a head ref
  • 1 commit
  • 5 files changed
  • 1 contributor

Commits on Aug 26, 2020

  1. Changed antialias config in downsample filter. Lots more initial work…

    … on OFDM filter but doesn't work yet.
    azonenberg committed Aug 26, 2020
    Copy the full SHA
    31a7e42 View commit details
Showing with 265 additions and 54 deletions.
  1. +1 −1 scopehal/Unit.cpp
  2. +2 −2 scopeprotocols/DownsampleFilter.cpp
  3. +244 −45 scopeprotocols/OFDMDemodulator.cpp
  4. +14 −2 scopeprotocols/OFDMDemodulator.h
  5. +4 −4 scopeprotocols/WindowedAutocorrelationFilter.cpp
2 changes: 1 addition & 1 deletion scopehal/Unit.cpp
Original file line number Diff line number Diff line change
@@ -221,7 +221,7 @@ double Unit::ParseString(string str)
scale = 1000.0;
else if(c == 'm')
scale = 0.001;
else if(c == 'u') //TODO: handle μ
else if( (c == 'u') || (str.find("μ", i) == i) )
scale = 1e-6;
else if(c == 'n')
scale = 1e-9;
4 changes: 2 additions & 2 deletions scopeprotocols/DownsampleFilter.cpp
Original file line number Diff line number Diff line change
@@ -110,10 +110,10 @@ void DownsampleFilter::Refresh()
auto din = GetAnalogInputWaveform(0);
size_t len = din->m_samples.size();

//Cut off all frequencies shorter than 1.5x our decimation factor
//Cut off all frequencies shorter than our decimation factor
int64_t factor = m_parameters[m_factorname].GetIntVal();
size_t outlen = len / factor;
float cutoff_period = factor * 1.5;
float cutoff_period = factor;
float sigma = cutoff_period / sqrt(2 * log(2));
int kernel_radius = ceil(3*sigma);

289 changes: 244 additions & 45 deletions scopeprotocols/OFDMDemodulator.cpp
Original file line number Diff line number Diff line change
@@ -49,15 +49,36 @@ OFDMDemodulator::OFDMDemodulator(string color)
m_min = FLT_MAX;
m_max = -FLT_MAX;

/*
m_windowName = "Window";
m_parameters[m_windowName] = FilterParameter(FilterParameter::TYPE_FLOAT);
m_parameters[m_windowName].SetFloatVal(0.4e-6);
m_periodName = "Period";
m_parameters[m_periodName] = FilterParameter(FilterParameter::TYPE_FLOAT);
m_parameters[m_periodName].SetFloatVal(3.6e-6);
*/
//TODO: create outputs

m_symbolTimeName = "Symbol Time";
m_parameters[m_symbolTimeName] = FilterParameter(FilterParameter::TYPE_INT, Unit(Unit::UNIT_PS));
m_parameters[m_symbolTimeName].SetIntVal(3.2e6);

m_guardIntervalName = "Guard Interval";
m_parameters[m_guardIntervalName] = FilterParameter(FilterParameter::TYPE_INT, Unit(Unit::UNIT_PS));
m_parameters[m_guardIntervalName].SetIntVal(400e3);

m_fftSizeName = "FFT Size";
m_parameters[m_fftSizeName] = FilterParameter(FilterParameter::TYPE_INT, Unit(Unit::UNIT_COUNTS));
m_parameters[m_fftSizeName].SetIntVal(64);

m_cachedFftSize = 0;
m_fftPlan = NULL;
m_fftInputBuf = NULL;
m_fftOutputBuf = NULL;

//Constant 16 point FFT
m_fftPlan16 = ffts_init_1d(16, FFTS_FORWARD);
}

OFDMDemodulator::~OFDMDemodulator()
{
ffts_free(m_fftPlan);
ffts_free(m_fftPlan16);

m_allocator.deallocate(m_fftInputBuf);
m_allocator.deallocate(m_fftOutputBuf);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -139,39 +160,37 @@ void OFDMDemodulator::Refresh()
auto din_i = GetAnalogInputWaveform(0);
auto din_q = GetAnalogInputWaveform(1);

//TODO
SetData(NULL, 0);
return;

/*
//Copy the units
m_yAxisUnit = m_channels[0]->GetYAxisUnits();
m_yAxisUnit = m_inputs[0].m_channel->GetYAxisUnits();

//Convert window and period to samples
float window_ps = m_parameters[m_windowName].GetFloatVal() * 1e12;
size_t window_samples = window_ps / din_i->m_timescale;
float period_ps = m_parameters[m_periodName].GetFloatVal() * 1e12;
size_t period_samples = period_ps / din_i->m_timescale;
window_samples = min(window_samples, period_samples);
//Convert rates to number of samples
int symbol_time_ps = m_parameters[m_symbolTimeName].GetIntVal();
size_t symbol_time_samples = symbol_time_ps / din_i->m_timescale;
int guard_interval_ps = m_parameters[m_guardIntervalName].GetIntVal();
size_t guard_interval_samples = guard_interval_ps / din_i->m_timescale;

//We need meaningful data, bail if it's too short
auto len = min(din_i->m_samples.size(), din_q->m_samples.size());
if(len < period_samples)
if(len < symbol_time_samples)
{
SetData(NULL);
SetData(NULL, 0);
return;
}

//Set up the output waveform
auto cap = new AnalogWaveform;
//Worry about the output later

//First pass: Find symbol sync (Schmidl-Cox)
//TODO: break up multi packet waveforms
//TODO: we can get a better idea of sync by looking at the *slope* of the metric, it should be flat topped.
//Find the left and right edges then look at the midpoint maybe?
size_t period_samples = symbol_time_samples + guard_interval_samples;
size_t end = len - 2*period_samples;
float vmax = -FLT_MAX;
float vmin = FLT_MAX;
float max_metric = -FLT_MAX;
size_t imax = 0;
for(size_t i=0; i < end; i ++)
{
complex<float> total = 0;
for(size_t j=0; j<window_samples; j++)
for(size_t j=0; j<guard_interval_samples; j++)
{
size_t first = i + j;
size_t second = first + period_samples;
@@ -182,26 +201,206 @@ void OFDMDemodulator::Refresh()
total += a*b;
}

float v = abs(total) / window_samples;
vmax = max(vmax, v);
vmin = min(vmin, v);
float metric = fabs(total);

if(metric > max_metric)
{
max_metric = metric;
imax = i;
}
}

//Create FFT config and buffers
int fftsize = m_parameters[m_fftSizeName].GetIntVal();
if(fftsize != m_cachedFftSize)
{
m_cachedFftSize = fftsize;

if(m_fftPlan)
ffts_free(m_fftPlan);
m_fftPlan = ffts_init_1d(fftsize, FFTS_FORWARD);

if(m_fftInputBuf)
m_allocator.deallocate(m_fftInputBuf);
m_fftInputBuf = m_allocator.allocate(fftsize*2);

cap->m_samples.push_back(v);
cap->m_offsets.push_back(din_i->m_offsets[i]);
cap->m_durations.push_back(din_i->m_durations[i]);
if(m_fftOutputBuf)
m_allocator.deallocate(m_fftOutputBuf);
m_fftOutputBuf = m_allocator.allocate(fftsize*2);
}

//Calculate bounds
m_max = max(m_max, vmax);
m_min = min(m_min, vmin);
m_range = (m_max - m_min) * 1.05;
m_offset = ( (m_max - m_min)/2 + m_min );
//everything after here is probably 802.11n specific for now?
if(fftsize != 64)
{
SetData(NULL, 0);
return;
}

//Copy our time scales from the input
cap->m_timescale = din_i->m_timescale;
cap->m_startTimestamp = din_i->m_startTimestamp;
cap->m_startPicoseconds = din_i->m_startPicoseconds;
//DEBUG: rely on scope trigger for initial sync
imax = 0;

SetData(cap);
*/
//Print header
static bool first = true;
if(first)
{
first = false;
LogDebug("symbol,");
for(int i=0; i<fftsize; i++)
LogDebug("i%d,q%d,", i, i);
LogDebug("\n");
}

//Read the short preamble. Ten blocks of 800ns symbols with no guard interval.
//We're sampling at 50ns per sample so this is 16 samples.
//Maybe we should do a 16 point FFT on each?
float preambleAmplitudes[12] = {0};
float initialPreamblePhases[12] = {0};
int preambleToneBins[12] = {1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15};
size_t unstableCount = 0; //number of preamble samples we consider unstable
//(ESP32s etc transmit early before full PLL lock!)
float phaseDriftPerCarrier[12] = {0};
for(size_t block=0; block<10; block ++)
{
//Copy I/Q sample data
size_t ibase = imax + 16*block;
for(int j=0; j<16; j++)
{
m_fftInputBuf[j*2] = din_i->m_samples[ibase + j];
m_fftInputBuf[j*2 + 1] = din_q->m_samples[ibase + j];
}

//Do the FFT
ffts_execute(m_fftPlan16, m_fftInputBuf, m_fftOutputBuf);

//Process each symbol
for(size_t i=0; i<12; i++)
{
//Average the amplitudes
int bin = preambleToneBins[i];
complex<float> c(m_fftOutputBuf[bin*2], m_fftOutputBuf[bin*2 + 1]);
preambleAmplitudes[i] += abs(c);

//Calculate inital phase
if(block == unstableCount)
initialPreamblePhases[i] = arg(c);
else if(block > unstableCount)
{
complex<float> d = polar(1.0f, arg(c) - initialPreamblePhases[i]);
if(block == 9)
{
phaseDriftPerCarrier[i] = arg(d) / (9 - unstableCount);
preambleAmplitudes[i] /= 10;

//New phase starts here
initialPreamblePhases[i] = arg(c);
}
}
}
}

LogDebug("0,");
for(size_t i=0; i<12; i++)
LogDebug("%12f,", phaseDriftPerCarrier[i]);
LogDebug("\n");

//Skip the short preamble
imax += 160;
float startingPhase = initialPreamblePhases[0];

//Calculate average drift across all preamble symbols
float driftPer800ns = 0;
float ampScale = 0;
for(size_t i=0; i<12; i++)
{
driftPer800ns += phaseDriftPerCarrier[i];
ampScale += preambleAmplitudes[i];
}
driftPer800ns /= 12;
ampScale /= 12;
float driftPerSymbol = 5 * driftPer800ns;

//Skip 1.6us (32 sample) guard interval
imax += 32;
startingPhase += 2*driftPer800ns;

//We should now have two OFDM BPSK training symbols with no guard interval
//Carrier in columns 8, 12, 16, 20, 24, 40, 44, 48, 52, 56, 60??
/*
for(size_t i=0; i<2; i++)
{
//Copy I/Q sample data
size_t ibase = imax + symbol_time_samples*i;
for(int j=0; j<fftsize; j++)
{
m_fftInputBuf[j*2] = din_i->m_samples[ibase + j];
m_fftInputBuf[j*2 + 1] = din_q->m_samples[ibase + j];
}
//Run the FFT
ffts_execute(m_fftPlan, m_fftInputBuf, m_fftOutputBuf);
//Grab each output
LogDebug("%zu,", i);
for(int j=0; j<fftsize; j++)
{
//Rotate by the calculated drift
complex<float> c(m_fftOutputBuf[j*2], m_fftOutputBuf[j*2 + 1]);
float phase = startingPhase + (4*driftPer800ns*i);
c *= polar(1.0f, -phase);
c /= ampScale;
LogDebug("%12f,%12f,", c.real(), c.imag());
}
LogDebug("\n");
}*/

//Skip these training symbols
imax += 128;
startingPhase += 8*driftPer800ns;

//Actual symbol data
/*
size_t iblock = 0;
for(size_t i=imax; i<end; i += period_samples)
{
//Copy I/Q sample data
//Initial sync point is the first symbol's cyclic prefix.
//Skip the guard interval when copying the actual samples.
size_t ibase = i + guard_interval_samples;
for(int j=0; j<fftsize; j++)
{
m_fftInputBuf[j*2] = din_i->m_samples[ibase + j];
m_fftInputBuf[j*2 + 1] = din_q->m_samples[ibase + j];
}
//Run the FFT
ffts_execute(m_fftPlan, m_fftInputBuf, m_fftOutputBuf);
LogDebug("%5zu,", iblock);
//Phase correction
for(size_t j=0; j<64; j++)
{
complex<float> c(m_fftOutputBuf[j*2], m_fftOutputBuf[j*2 + 1]);
//Track pilot tone in bin 7
//complex<float> pilot(m_fftOutputBuf[7*2], m_fftOutputBuf[7*2 + 1]);
//float phase = arg(pilot);
float phase = startingPhase + driftPerSymbol*iblock;
c *= polar(1.0f, -phase);
c /= ampScale;
LogDebug("%12f,%12f,", c.real(), c.imag());
}
LogDebug("\n");
iblock ++;
break;
}*/

//TODO
SetData(NULL, 0);
return;
}
16 changes: 14 additions & 2 deletions scopeprotocols/OFDMDemodulator.h
Original file line number Diff line number Diff line change
@@ -35,10 +35,13 @@
#ifndef OFDMDemodulator_h
#define OFDMDemodulator_h

#include <ffts/ffts.h>

class OFDMDemodulator : public Filter
{
public:
OFDMDemodulator(std::string color);
virtual ~OFDMDemodulator();

virtual void Refresh();

@@ -62,8 +65,17 @@ class OFDMDemodulator : public Filter
float m_min;
float m_max;

std::string m_windowName;
std::string m_periodName;
ffts_plan_t* m_fftPlan;
ffts_plan_t* m_fftPlan16;
float* m_fftInputBuf;
float* m_fftOutputBuf;
int m_cachedFftSize;

AlignedAllocator<float, 64> m_allocator;

std::string m_symbolTimeName;
std::string m_guardIntervalName;
std::string m_fftSizeName;
};

#endif
Loading