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

    Unverified

    This user has not yet uploaded their public signing key.
    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