Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
I collected the instruments of digital signal processing around me, …
Browse files Browse the repository at this point in the history
…that I might infuse a spark of being into the lifeless thing that lay at my feet.

I tweaked the hell out of the digital signal processing code to get gen3
to work pretty well on my nexus5. gen2 still works great!
  • Loading branch information
jfly committed Jul 29, 2014
1 parent 71cdd8c commit 9055a9d
Show file tree
Hide file tree
Showing 15 changed files with 3,569 additions and 3,296 deletions.
4 changes: 3 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
Stackmat.initialize("release/fskube.js");
Stackmat.onstackmatstate = function(state) {
if(state.on) {
$("#counter").text(msToClock(state.millis, state.generation == 3));
// We seem to have issues decoding signals where both hands are down.
// This is a hack to ignore those signals and not update the timer.
$("#counter").text(state.lf + " " + state.cr + " " + state.checksum + " " + state.computedChecksum + " " + msToClock(state.millis, state.generation == 3));
} else {
$("#counter").text("");
}
Expand Down
6,741 changes: 3,473 additions & 3,268 deletions release/fskube.js

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion src/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ static unsigned int samplesUntilOff;
void fskube_initialize(unsigned int sampleRate) {
FskParams fsk;
fsk.samplesPerSecond = sampleRate;
fsk.bitsPerSecond = 1200;

// The baud rate of a gen2 timer is 1200 Hz, and the baud rate of a gen3 timer
// is 1220 Hz (even when it sends a gen2 signal). Ideally, we'd have demodulators
// set up for both baud rates, but gen2 interpretation seems to work fine if we
// set the baud rate to 1220bps.
fsk.bitsPerSecond = 1220;

fsk.markFrequency = 1200;
fsk.spaceFrequency = 2200;
demodulator.setFskParams(fsk);
Expand Down
4 changes: 4 additions & 0 deletions src/embind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ EMSCRIPTEN_BINDINGS(my_module) {
value_object<StackmatState>("StackmatState")
.field("millis", &StackmatState::millis)
.field("generation", &StackmatState::generation)
.field("checksum", &StackmatState::checksum)
.field("computedChecksum", &StackmatState::computedChecksum)
.field("lf", &StackmatState::lf)
.field("cr", &StackmatState::cr)
.field("commandByte", &StackmatState::commandByte)
.field("on", &StackmatState::on)
;
Expand Down
67 changes: 53 additions & 14 deletions src/fsk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Demodulator::Demodulator(FskParams fsk) {
}

void Demodulator::setFskParams(FskParams fsk) {
assert(fsk.markFrequency < fsk.spaceFrequency);
this->fsk = fsk;
}

Expand All @@ -56,6 +57,9 @@ void Demodulator::reset() {

lastFrequencyHalfSeen = 0;
lastFrequencyHalfSeenCount = 0;

nearMarks = 0;
currentMarkStreak = 0;
}

void Demodulator::flush() {
Expand All @@ -80,6 +84,7 @@ void Demodulator::receive(double value) {

Sample sample;
sample.index = sampleIndex++;
sample.remainder = 0;
sample.value = value;
sample.valid = true;

Expand All @@ -95,7 +100,7 @@ void Demodulator::receive(double value) {
// Quick bootstrapping. If we haven't seen any "significant"
// signals yet, and this one is close to zero (ie:
// "insignificant"), treat it as a zero. Note that we don't
// call _addZeroCrossing() here, as we may have a bunch
// call addZeroCrossing() here, as we may have a bunch
// of zeros in a row, and we don't want them to be treated as
// half periods of our signal.
if(!lastSignificantSample.valid) {
Expand All @@ -122,10 +127,16 @@ void Demodulator::receive(double value) {
// -lastValue*index + lastValue*C = value*C - value*lastIndex
// -lastValue*index + value*lastIndex = value*C - C*lastValue
// C = (value*lastIndex - lastValue*index) / (value - lastValue)
unsigned long long crossingIndex =

// TODO - this should be changed to do integer math for speed and accuracy
// Even at 48kHz sampling, it should take hundreds of years before
// we start to lose precision with a double.
// TODO - store samples as ints in some range rather than doubles
double crossingIndex =
(value*lastIndex - lastValue*index) / (value - lastValue);
Sample crossingSample;
crossingSample.index = crossingIndex;
crossingSample.remainder = (crossingIndex - (int) crossingIndex);
crossingSample.value = 0;
crossingSample.valid = true;
// We crossed the x axis in a "significant" way!
Expand All @@ -139,16 +150,16 @@ void Demodulator::receive(double value) {

/**
* Given the index at which a zero crossing occurred, determine
* the current frequency of the signal.
* If the frequency is nonsensical, reset everything.
* If the frequency is near the mark or space frequency,
* call either addMarkFrequencySeen() or addSpaceFrequencySeen().
* the current frequency of the signal. If the frequency is clear,
* call addFrequencyHalfSeen(), if it's unclear, then do some special
* stuff to make it more likely to see marks (the signal with a lower frequency).
*/
void Demodulator::addZeroCrossing(Sample sample) {
LOG1("Zero crossing! @%llu (last one was at %llu isValid: %d)",
sample.index, lastZeroCrossing.index, lastZeroCrossing.valid);
LOG1("Zero crossing! @%llu+%f (last one was at %llu+%f isValid: %d)",
sample.index, sample.remainder, lastZeroCrossing.index, lastZeroCrossing.remainder, lastZeroCrossing.valid);
if(lastZeroCrossing.valid) {
double crossingTimeDelta = fsk.samplesToTime(sample.index - lastZeroCrossing.index);
double samples = (sample.index - lastZeroCrossing.index) + (sample.remainder - lastZeroCrossing.remainder);
double crossingTimeDelta = fsk.samplesToTime(samples);
// 1/2th the hZ of a signal is its expected amount of time
// between zero crossings.
double markCrossingTime = 0.5/fsk.markFrequency;
Expand All @@ -160,10 +171,28 @@ void Demodulator::addZeroCrossing(Sample sample) {
LOG1("Zero crossing of %f seconds (distanceToMark: %f distanceToSpace: %f)",
crossingTimeDelta, distanceToMark, distanceToSpace);

float MAX_DISTANCE = 0.15;
if(distanceToMark <= MAX_DISTANCE) {
// If the measured frequency is within 10% of the target mark or space
// frequency, call addFrequencyHalfSeen(). If not, keep track of
// frequencies within 20% of the mark (this are called nearMarks).
// If we witness a nearMark without ever seeing a "perfect"
// (within 10%) mark (currentMarkStreak == 0), then we send out a mark.
// Also, if we witness 2 nearMarks, fire a mark. Unfortunately, this
// logic is pretty hardcoded for markFrequency=1200Hz and
// spaceFrequency=2200Hz.
float MAX_PERFECT_DISTANCE = 0.10;
float MAX_NEAR_DISTANCE = 0.20;
if(distanceToMark <= MAX_PERFECT_DISTANCE) {
currentMarkStreak++;
addFrequencyHalfSeen(fsk.markFrequency);
} else if(distanceToSpace <= MAX_DISTANCE) {
} else if(distanceToSpace <= MAX_PERFECT_DISTANCE) {
if(currentMarkStreak == 0 && nearMarks >= 1) {
bool bit = fsk.frequencyToBit(fsk.markFrequency);
LOG2("sending bit %d", bit);
send(bit);
nearMarks = 0;
}
currentMarkStreak = 0;
nearMarks = 0;
addFrequencyHalfSeen(fsk.spaceFrequency);
} else {
LOG1("Ignoring zero crossing");
Expand All @@ -173,14 +202,24 @@ void Demodulator::addZeroCrossing(Sample sample) {
bool bit = fsk.frequencyToBit(lastFrequencyHalfSeen);
LOG2("sending bit %d", bit);
send(bit);
} else {
if(distanceToMark <= MAX_NEAR_DISTANCE) {
nearMarks++;
if(nearMarks == 2) {
bool bit = fsk.frequencyToBit(fsk.markFrequency);
LOG2("sending bit %d", bit);
send(bit);
nearMarks = 0;
}
}
}
lastFrequencyHalfSeen = 0;
lastFrequencyHalfSeenCount = 0;
}
}
lastZeroCrossing = sample;
LOG1("Zero crossing is now %llu isValid: %d",
lastZeroCrossing.index, lastZeroCrossing.valid);
LOG1("Zero crossing is now %llu+%f isValid: %d",
lastZeroCrossing.index, lastZeroCrossing.remainder, lastZeroCrossing.valid);
}

void Demodulator::addFrequencyHalfSeen(unsigned int frequency) {
Expand Down
6 changes: 6 additions & 0 deletions src/fsk.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct FskParams {
double samplesToTime(unsigned int samples) {
return (double) samples / samplesPerSecond;
}
double samplesToTime(double samples) {
return samples / samplesPerSecond;
}
bool frequencyToBit(unsigned int frequency) {
return frequency == markFrequency ? 1 : 0;
}
Expand All @@ -39,6 +42,7 @@ class Modulator : public Sender<bool, double> {

struct Sample {
unsigned long long index;
float remainder;
double value;
bool valid;
};
Expand All @@ -64,6 +68,8 @@ class Demodulator : public Sender<double, bool> {
void reset();
void addZeroCrossing(Sample sample);
void addFrequencyHalfSeen(unsigned int frequency);
int nearMarks;
int currentMarkStreak;
public:
Demodulator();
Demodulator(FskParams fsk);
Expand Down
19 changes: 10 additions & 9 deletions src/stackmat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ void StackmatInterpreter::receive(int byte) {
} else {
thousandthsDigit = 0;
}
char checksum = receivedBytes[i++];
char cr = receivedBytes[i++];
char lf = receivedBytes[i++];
unsigned char checksum = receivedBytes[i++];
unsigned char lf = receivedBytes[i++];
unsigned char cr = receivedBytes[i++];

state.millis = 0;
state.millis += minuteDigit * MILLIS_PER_MINUTE;
Expand All @@ -116,16 +116,17 @@ void StackmatInterpreter::receive(int byte) {
state.millis += hundredthsDigit * MILLIS_PER_CENTISECOND;
state.millis += thousandthsDigit;

char computedChecksum = 64 + minuteDigit + tensSecondsDigit +
unsigned char computedChecksum = 64 + minuteDigit + tensSecondsDigit +
onesSecondsDigit + tenthsDigit + hundredthsDigit +
thousandthsDigit;
bool valid = (computedChecksum == checksum) && (cr == '\r') && (lf == '\n');
state.checksum = checksum;
state.computedChecksum = computedChecksum;
state.lf = lf;
state.cr = cr;

receivedBytesLength = 0;
if(valid) {
state.on = true;
send(state);
}
state.on = true;
send(state);
break;
}
default:
Expand Down
4 changes: 4 additions & 0 deletions src/stackmat.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ namespace fskube {

struct StackmatState {
bool on;
unsigned char checksum;
unsigned char computedChecksum;
unsigned char lf;
unsigned char cr;
unsigned int millis;
unsigned int generation;
unsigned char commandByte;
Expand Down
2 changes: 1 addition & 1 deletion test/data/0.000_nexus5.testdata
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# 44100Hz 1200Hz 2200Hz 1220bps
# "000000000000000000000000000001011011010111110011011111001101111100110111110011011111001101111100110111111101011010111101010011110000000000000000000000000000000000000000000000000000000010110110101111100110111110011011111001101111100110111110011011111001101111111010110101111010100111100000000000000000000000000000000000000000000000000000000101101101011111001101111100110111110011011111001101"
# "0000000000000000000000000001011011010111110011011111001101111100110111110011011111001101111100110111111101011010111101010011110000000000000000000000000000000000000000000000000000000010110110101111100110111110011011111001101111100110111110011011111001101111111010110101111010100111100000000000000000000000000000000000000000000000000000000101101101011111001101111100110111110011011111001101"
# 0.000_nexus5.wav
3 changes: 3 additions & 0 deletions test/data/1.774_bothhands_nexus5.testdata
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 44100Hz 1200Hz 2200Hz 1220bps
# "01001011010011011101101011010111101010111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110010111110011010111001110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011011111001101011100110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110010111110011010111001110100010011010001001101110100110100110101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110111110011010111001101000100110100010011011101001101001101010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101111100110101110011010001001101000100110111010011010011010101101011110101001111000000000000000000000000"
# 1.774_bothhands_nexus5.wav
Binary file added test/data/1.774_bothhands_nexus5.wav
Binary file not shown.
3 changes: 3 additions & 0 deletions test/data/11.233_nexus5.testdata
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 44100Hz 1200Hz 2200Hz 1220bps
# "010100111100000000000000000000000000000000000000000000000000000000100111101011111001101011100110101110011011011001101001100110100110011011010110101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011010111001101011100110110110011010011001101001100110110101101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110101110011010111001101101100110100110011010011001101101011010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101011100110101110011011011001101001100110100110011011010110101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011010111001101011100110110110011010011001101001100110110101101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110101110011010111001101101100110100110011010011001101101011010110101111010100111100000000000000000000000000000000000000000000000000000000100111101011111001101011100110101110011011011001101001100110100110011011010110101101011110101001111000000000000000000000000000000000000000000000000000000001001111010111110011010111001101011100110110110011010011001101001100110110101101011010111101010011110000000000000000000000000000000000000000000000000000000010011110101111100110101110011010111"
# 11.233_nexus5.wav
Binary file added test/data/11.233_nexus5.wav
Binary file not shown.
2 changes: 1 addition & 1 deletion test/data/in.testdata
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@
253 0
254 0
255 0
256 0
256 -0.5
257 0.17013929784297943
258 0.33531734347343445
259 0.4907175600528717
Expand Down
2 changes: 1 addition & 1 deletion test/data/in1.testdata
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 44100Hz 1200Hz 2400Hz 1200bps
# [1]
0 0
0 -0.5
1 0.17013929784297943
2 0.33531734347343445
3 0.4907175600528717
Expand Down

0 comments on commit 9055a9d

Please sign in to comment.