Skip to content

Instantly share code, notes, and snippets.

@cj-dimaggio
Last active March 15, 2017 23:08
Show Gist options
  • Save cj-dimaggio/e0e6023b5c6e7016e9f09a6a4299aede to your computer and use it in GitHub Desktop.
Save cj-dimaggio/e0e6023b5c6e7016e9f09a6a4299aede to your computer and use it in GitHub Desktop.
Blink Decode
package main
import (
"math/rand"
"os"
"strconv"
"time"
"math"
"github.com/fatih/color"
)
func randomBetween(min, max int) int {
rand.Seed(time.Now().Unix())
return rand.Intn(max-min) + min
}
var idLength = 8
// Notice how we only go up to 254; 255 is *not* a valid ID
var id = uint(randomBetween(0, int(math.Pow(2, float64(idLength))-2)))
var hz = 10
var waitDur = time.Second / time.Duration(hz)
var clockAlignRate = time.Duration(10000)
var quit = make(chan int)
// Go has no asserts!
func assert(cond bool, desc string) {
if !cond {
color.Red(desc)
}
}
func getBit(val uint, index uint) bool {
if index >= uint(idLength) {
color.Red("Tried to get an invalid bit! Quitting because we're scared of you")
quit <- 1
}
mask := uint(1 << index)
bin := (val & mask) / mask
if bin == 1 {
return true
}
return false
}
func setBit(byt uint, index uint, val bool) uint {
if val {
return byt | (1 << index)
}
return byt
}
func nextPeriod() {
time.Sleep(waitDur)
}
func printBin(prefix string, val bool, print func(format string, a ...interface{})) {
var bin int
if val {
bin = 1
} else {
bin = 0
}
print("%s: %d\n", prefix, bin)
}
func emit(val bool) {
printBin("Emitting", val, color.Green)
state = val
nextPeriod()
}
func receive() bool {
val := state
printBin("Received", val, color.Blue)
nextPeriod()
return val
}
// This represents the state of the "wire" (obviously this is volatile; that's the point)
var state = false
func signal(id uint) {
for {
// We start by printing out a list of 1s for the length of our id times 2
// to signify a message is about to start
for i := 0; i < idLength; i++ {
emit(true)
emit(true)
}
// Start our next sequence with a zero
emit(false)
// Now we can emit our actual binary
for i := 0; i < idLength; i++ {
// In order to keep the positional tracker every bit is prepended by an on signal
emit(true)
emit(getBit(id, uint(i)))
}
// We end our sequence with a zero
emit(false)
}
}
func alignClock(pollFreq time.Duration) {
color.Yellow("Trying to align clocks")
// Unfortunately, in order to save space, we probably can't use a clock syncing
// encoding such as the Manchester code. So that means we need to try to sync our
// clock as best as we can. How can we do that? Well I guess we can just poll very
// fast and wait for a state change to tell us when to kick in. It's not pretty;
// we'll burn CPU; any better suggestions?
//
// (this along with alignReceiver means we can miss as many as two sequence broadcasts)
val := state
for {
// Instead of polling as fast as we can, let's only do it at a fraction of our hz.
// (we're working at such a low hz this actually makes a difference)
time.Sleep(waitDur / pollFreq)
if state != val {
// Okay, we just witnessed a state change, let's wait half a period so we don't have
// *as* bad of a race condition
time.Sleep(waitDur / 2)
color.Yellow("Clocks might be aligned")
return
}
}
}
func alignReceiver() {
var val bool
// First things first, we need to align ourselves with the output before we can reliably
// parse
onesCounted := 0
// Go has no "while" loop!
for onesCounted < (idLength * 2) {
val = receive()
if val {
onesCounted++
} else {
onesCounted = 0
}
}
color.Yellow("Receiver should now be aligned")
}
func receiverLoop() {
// Okay, if we've gotten here we should have a good idea where we are. We just received a long
// sequence of 1s and we can take it from there
for {
val := uint(0)
assert(receive() == false, "Expecting a 0")
for i := 0; i < idLength; i++ {
// Here comes our sequence
assert(receive() == true, "Expecting a 1")
val = setBit(val, uint(i), receive())
}
color.Cyan("Is the id: %d : %08b?", val, val)
assert(receive() == false, "Expecting a 0")
for i := 0; i < idLength; i++ {
assert(receive() == true, "Expecting a 1")
assert(receive() == true, "Expecting a 1")
}
}
}
func receiver() {
alignClock(clockAlignRate)
alignReceiver()
receiverLoop()
}
// This is really shit
func handleArgs() {
if len(os.Args) > 1 {
_hz, _ := strconv.ParseInt(os.Args[1], 10, 0)
hz = int(_hz)
waitDur = time.Second / time.Duration(hz)
if len(os.Args) > 2 {
_idLength, _ := strconv.ParseInt(os.Args[2], 10, 0)
idLength = int(_idLength)
id = uint(randomBetween(0, int(math.Pow(2, float64(idLength))-2)))
if len(os.Args) > 3 {
_clockAlignRate, _ := strconv.ParseInt(os.Args[3], 10, 0)
clockAlignRate = time.Duration(_clockAlignRate)
}
}
}
}
func main() {
handleArgs()
color.Magenta("ID is: %d : %08b\n", id, id)
color.Yellow("Starting signal...")
go signal(id)
color.Yellow("Sleeping a random amount to see if it screws us up...")
time.Sleep(time.Duration(randomBetween(0, 3000)) * time.Millisecond)
color.Yellow("Starting receiver...")
go receiver()
os.Exit(<-quit)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment