# demo.py

# Raspberry Pi Pico - Signal Processing Demo

# Read an analog input with the ADC, apply various filters, and print filtered
# data to the console for plotting.

# Import CircuitPython modules.
import board
import time
import analogio
import digitalio

# Import every filter sample.  These files should be copied to the top-level
# directory of the CIRCUITPY filesystem on the Pico.

import biquad
import hysteresis
import linear
import median
import smoothing
import statistics

#---------------------------------------------------------------
# Set up the hardware.

# Set up an analog input on ADC0 (GP26), which is physically pin 31.
# E.g., this may be attached to photocell or photointerrupter with associated pullup resistor.
sensor = analogio.AnalogIn(board.A0)

#---------------------------------------------------------------
# Initialize filter objects as global variables.

stats        = statistics.CentralMeasures()
hysteresis   = hysteresis.Hysteresis(lower=0.25, upper=0.75)
average      = smoothing.MovingAverage()
smoothing    = smoothing.Smoothing()
median       = median.MedianFilter()
lowpass      = biquad.BiquadFilter(biquad.low_pass_10_1)
highpass     = biquad.BiquadFilter(biquad.high_pass_10_1)
bandpass     = biquad.BiquadFilter(biquad.band_pass_10_1)
bandstop     = biquad.BiquadFilter(biquad.band_stop_10_1)

# Collect all the filters in a list for efficient multiple updates.

all_filters = [ stats, hysteresis, average, smoothing, median,
                lowpass, highpass, bandpass, bandstop, ]

#---------------------------------------------------------------
# Run the main event loop.

# Use the high-precision clock to regulate a precise *average* sampling rate.
sampling_interval  = 100000000           # 0.1 sec period of 10 Hz in nanoseconds
next_sample_time   = time.monotonic_ns()

while True:
    # read the current nanosecond clock
    now = time.monotonic_ns()
    if now >= next_sample_time:
        # Advance the next event time; by spacing out the timestamps at precise
        # intervals, the individual sample times may have 'jitter', but the
        # average rate will be exact.
        next_sample_time += sampling_interval
        
        # Read the sensor once per sampling cycle.
        raw = sensor.value

        # Apply calibration to map integer ADC values to meaningful units.  The
        # exact scaling and offset will depend on both the individual device and
        # application.
        calib  = linear.map(raw, 59000, 4000, 0.0, 1.0)

        # Pipe the calibrated value through all the filters.
        filtered = [filt.update(calib) for filt in all_filters]

        # Selectively report results for plotting.
        # print((raw, calib))                            # raw and calibrated input signal
        # print((calib, filtered[0][0], filtered[0][1])) # calibrated, average, variance
        # print((calib, 1*filtered[1], filtered[2]))     # calibrated, thresholded, moving average
        print((calib, filtered[3], filtered[4]))         # calibrated, smoothed, median-filtered
        # print((calib, filtered[5], filtered[6]))       # calibrated, low-pass, high-pass
        # print((calib, filtered[7], filtered[8]))       # calibrated, band-pass, band-stop
