import sys, os, time
from optparse import OptionParser
from nco_utils import *

"""
This demo uses helper functions defined in `nco_utils.py`. Those helper functions demonstrate how to use the digital down and up conversion
capabilities of the RFDC via casperfpga. The intention of this demo is to not use the helper functions as they are here. Rather, this demos
is only to show the output of exercising the digital down converters. Instead, the intention is to examine the helper functions in
`nco_utils.py` and learn how to use the casperfpga methods in other application settings.
"""

if __name__=="__main__":
  p = OptionParser()
  p.set_usage('nco_demo.py <HOSTNAME_or_IP> [options]')
  p.add_option('-s', '--skip', dest='skip', action='store_true',
      help='Skip programming and begin to plot data')

  # get board hostname or ip address
  opts, args = p.parse_args(sys.argv[1:])
  if len(args) < 1:
    print('Specify a hostname or IP for your casper platform.\n'
          'Run with the -h flag to options.')

    sys.exit()
  else:
    hostname = args[0]

  # check for fpg file
  fpgfile = './nco_demo_2025-10-03_1023.fpg'
  if not os.path.exists(fpgfile):
    print("This script must run from the same directory where {:s} is located".format(fpgfile))
    sys.exit(1)

  # connect and program
  print('Connecting to {:s}'.format((hostname)))
  z = casperfpga.CasperFpga(hostname)
  if not opts.skip:
    print("Programming fpga...")
    z.upload_to_ram_and_program(fpgfile)
    time.sleep(0.2)
  else:
    print("Skipped programming...")
    z.get_system_information()

  rfdc = z.adcs.rfdc
  if not opts.skip:
    rfdc.init()

  # reset nco parameters for the demo
  set_adc_nco(z, 0)
  set_dac_nco(z, 0)

  # set DACs output waveform source to be a complex constant. When passed through the
  # digital upconverter this results in a CW tone at the NCO frequency of the DAC.
  set_dac_src(z, 1)

  print("\nClose each plot to advance to subsequent examples\n")

  print("\nADC Sample rate is 1000 MHz\n")

  # Example 1
  dac_nco_freq = 200
  adc_nco_freq = 0
  print("\nExample 1: Baseband ADC setting (no downconversion)")
  print(f"DAC NCO: {dac_nco_freq} MHz")
  print(f"ADC NCO: {adc_nco_freq} MHz")

  set_dac_nco(z, dac_nco_freq)

  X = get_adc_samples(z)
  plot_PSD(X, adc_nco_freq)
  
  # Example 2
  print("\nExample 2: Downconvert ADC setting")
  dac_nco_freq = 200
  adc_nco_freq = -100
  print(f"DAC NCO: {dac_nco_freq} MHz")
  print(f"ADC NCO: {adc_nco_freq} MHz")

  set_dac_nco(z, dac_nco_freq)
  set_adc_nco(z, adc_nco_freq)

  time.sleep(0.4)
  X = get_adc_samples(z)
  plot_PSD(X, adc_nco_freq)

  # Example 3
  print("\nExample 3: Downconvert by fs/2 ADC setting")
  dac_nco_freq = 200
  adc_nco_freq = -500
  print(f"DAC NCO: {dac_nco_freq} MHz")
  print(f"ADC NCO: {adc_nco_freq} MHz")

  set_dac_nco(z, dac_nco_freq)
  set_adc_nco(z, adc_nco_freq)

  time.sleep(0.4)
  X = get_adc_samples(z)
  plot_PSD(X, adc_nco_freq)

  # Example 4
  print("\nExample 4: Sweep CW through ADC downconversion set to fs/2")
  dac_nco_freq = [100, 200, 300, 420, 600, 650, 800]
  adc_nco_freq = -500

  print(f"ADC NCO: {adc_nco_freq} MHz")
  set_adc_nco(z, adc_nco_freq)
  for df in dac_nco_freq:
    print(f"DAC NCO: {df} MHz")

    set_dac_nco(z, df)

    time.sleep(0.4)
    X = get_adc_samples(z)
    plot_PSD(X, adc_nco_freq)
