#!/usr/bin/python3

### Parameters
# Sound output parameters
volume = 1.0
sample_buf_size = 44
sampling_freq = 44100 #Hz

# Frequency generator parameters
min_freq = 200  #Hz
max_freq = 2000 #Hz

# Proxmark3 parameters
pm3_client="/usr/local/bin/proxmark3"
pm3_reader_dev_file="/dev/ttyACM0"
pm3_tune_cmd="hf tune"


### Modules
import numpy
import pyaudio
from select import select
from subprocess import Popen, DEVNULL, PIPE


### Main program
p = pyaudio.PyAudio()

# For paFloat32 sample values must be in range [-1.0, 1.0]
stream = p.open(format=pyaudio.paFloat32,
    channels=1,
    rate=sampling_freq,
    output=True)

# Initial voltage to frequency values
min_v = 100.0
max_v = 0.0
v = 0
out_freq = min_freq

# Spawn the Proxmark3 client
pm3_proc = Popen([pm3_client, pm3_reader_dev_file, "-c", pm3_tune_cmd],
    bufsize=0, env={}, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL)
mv_recbuf = ""

# Read voltages from the Proxmark3, generate the sine wave, output to soundcard
sample_buf = [0.0 for x in range(0, sample_buf_size)]
i = 0
sinev = 0
while True:

  # Read Proxmark3 client's stdout and extract voltage values
  if(select([pm3_proc.stdout], [], [], 0)[0]):

    b = pm3_proc.stdout.read(256).decode("ascii")
    if "Done" in b:
        break;
    for c in b:
      if c in "0123456789 mV":
        mv_recbuf += c
      else:
        mv_recbuf = ""
      if mv_recbuf[-3:] == " mV":
        v = int(mv_recbuf[:-3]) / 1000
        if v < min_v:
          min_v = v - 0.001
        if v > max_v:
          max_v = v

        # Recalculate the audio frequency to generate
        out_freq = (max_freq - min_freq) * (max_v - v) / (max_v - min_v) \
        + min_freq

  # Generate the samples and write them to the soundcard
  sinevs = out_freq / sampling_freq * numpy.pi * 2
  sample_buf[i] = sinev
  sinev += sinevs
  sinev = sinev if sinev < numpy.pi * 2 else sinev - numpy.pi * 2
  i = (i + 1) % sample_buf_size
  if not i:
    stream.write((numpy.sin(sample_buf) * volume).
        astype(numpy.float32).tobytes())
