# -*- coding: utf-8 -*-
# Copyright (C) 2009 Donald L. Dietmeyer (dldietmeyer@charter.net)
# Copyright (C) 2011 DF9DQ
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  You may view a copy of the
# GNU GPL at  http://www.gnu.org/copyleft/gpl.html  or Google GNU GPL.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# This driver is for the FiFi-SDR receiver. For more info visit
# the website at http://o28.sischa.net/fifisdr/trac/wiki
# If you want to use this hardware module, then specify it in your
# .quisk_conf.py file with:
# import quisk_hardware_fifisdr as quisk_hardware

from quisk_hardware_model import Hardware as BaseHardware
import usb     # Provide USB access to Python
               # Ubuntu: load  python-usb  0.4.1-ubuntu1
               # Web:  http://sourceforge.net/projects/pyusb/

USBDEV_VENDOR  = 0x16C0
USBDEV_PRODUCT = 0x05DC
VENDOR_NAME    = "www.ov-lennestadt.de"
PRODUCT_NAME   = "FiFi-SDR"

MULTIPLIER     = 4
CONVERT_11_21  = 2097152.0                       # 11.21 format
CONVERT        = MULTIPLIER * CONVERT_11_21
CONVERT_8_24   = 16777216.0
MEGA           = 1000000.0

GET_VERSION      = 0x00
SET_FREQ         = 0x32
SET_XTAL_FREQ    = 0x33
SET_STARTUP_FREQ = 0x34
GET_SUB_MULT     = 0x39
GET_FREQ         = 0x3A
GET_STARTUP_FREQ = 0x3C
GET_XTAL_FREQ    = 0x3D
SET_PTT          = 0x50
GET_FIFI_EXTRA   = 0xAB
SET_FIFI_EXTRA   = 0xAC

EXTRA_READ_SVN_VERSION          = 0
EXTRA_WRITE_PREAMP              = 19


DEVICE2HOST = 0xC0  # Device to Host, Vendor, to Device
HOST2DEVICE = 0x40  # Host to Device, Vendor, to Device


class Hardware(BaseHardware):
    def __init__(self, app, conf):
        BaseHardware.__init__(self, app, conf)
        self.conf = conf
        self.frequency = 7e6
        self.tune = self.frequency
        self.handle = None
        self.TUNEMAX = self.conf.sample_rate / 2 - 1000
        self.looffset = 0                               # LO offset (see GET_SUB_MULT command)
        self.rf_gain_labels = ('0 dB', '-6 dB')         # 0/-6 dB switch in ADC

    def open(self):			        # Called once to open the Hardware
        for bus in usb.busses():
            for dev in bus.devices:
                h = dev.open()
                if (dev.idVendor == USBDEV_VENDOR) and (dev.idProduct == USBDEV_PRODUCT):
                    print "Connected to FiFi-SDR"
                    self.handle = h
                    self.looffset = self.getLoOffset()
                    self.getStartup()
                    self.getSoftrockVersion()
                    self.getSvnVersion()
                    self.frequency = self.getFrequency()
                    self.tune = self.frequency + MULTIPLIER*12000
                    self.getXtal()
                    return
        print "Can't open the FiFi-SDR"
        exit()

    def close(self):			# Called once to close the Hardware
        self.handle = self.handle.reset()

    def ChangeFrequency(self, tune, vfo, source='', band='', event=None):
        # Change and return the tuning and VFO frequency in Hertz.  The VFO frequency is the
        # frequency in the center of the display; that is, the RF frequency corresponding to an
        # audio frequency of zero Hertz.  The tuning frequency is the RF frequency indicated by
        # the tuning line on the display, and is equivalent to the transmit frequency.  The quisk
        # receive frequency is the tuning frequency plus the RIT (receive incremental tuning).
        # If your hardware will not change to the requested frequencies, return different
        # frequencies.
        # The source is a string indicating the source of the change:
        #   BtnBand       A band button
        #   BtnUpDown     The band Up/Down buttons
        #   FreqEntry     The user entered a frequency in the box
        #   MouseBtn1     Left mouse button press
        #   MouseBtn3     Right mouse button press
        #   MouseMotion   The user is dragging with the left button
        #   MouseWheel    The mouse wheel up/down
        # For "BtnBand", the string band is in the band argument.
        # For the mouse events, the handler event is in the event argument.

        # If you drag the cursor to the far left or right,
        # adapt the the center frequency by +/- 10 kHz.
        if abs(tune - vfo) >= self.TUNEMAX:
            if tune > vfo:
                vfo = vfo + 10000
                tune = vfo + self.TUNEMAX - 10000
            else:
                vfo = vfo - 10000
                tune = vfo - self.TUNEMAX + 10000
        self.tune = tune

        if vfo != self.frequency:
            self.frequency = vfo
            v = vfo / MEGA              # Make it MHz
            vset = v + self.looffset    # Account for the programmed offset

            if (vset >= 0.04) and (vset <= 200.0):
                try:
                    n = self.handle.controlMsg(requestType = HOST2DEVICE,
                                               request = SET_FREQ,
                                               buffer = self.pack4(int(vset * CONVERT)),
                                               value=0,
                                               index=0,
                                               timeout=100)
                    if (vset != v):
                        print "LO set to %f MHz (real: %f MHz)" % (v, vset)
                    else:
                        print "LO set to %f MHz" % v
                except usb.USBError:
                    print "FiFi-SDR communication error"
                    exit()

            else:
                print "Desired frequency (%f) is out of range!" % vset
                return 0, 0
        return tune, self.frequency

    def pack1(self, integer):
        buf = ''
        int = integer
        for i in range(1):
            buf += chr(int & 0xff)
            int >>= 8
        return buf

    def pack4(self, integer):
        buf = ''
        int = integer
        for i in range(4):
            buf += chr(int & 0xff)
            int >>= 8
        return buf

    def ReturnFrequency(self):
        # Return the current tuning and VFO frequency.  If neither have changed,
        # you can return (None, None).  This is called at about 10 Hz by the main.
        # return (tune, vfo)	# return changed frequencies
        return self.tune, self.frequency

    def ChangeMode(self, mode):		# Change the tx/rx mode
        # mode is a string: "USB", "AM", etc.
        pass
    def ChangeBand(self, band):		# Change the band
        # band is a string: "60", "40", "WWV", etc.
        pass
    def HeartBeat(self):	# Called at about 10 Hz by the main
        pass

    def getFrequency(self):
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = GET_FREQ,
                                     buffer = 4,
                                     value=0,
                                     index=0,
                                     timeout=100)
        f = ((((((res[3]<<8) + res[2])<<8) + res[1])<<8) + res[0]) / CONVERT
        f = f - self.looffset
        print "LO frequency is %f MHz" % f
        return int(f * MEGA)   # In Hz

    def getStartup(self):
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = GET_STARTUP_FREQ,
                                     buffer = 4,
                                     value=0,
                                     index=0,
                                     timeout=100)
        f = ((((((res[3]<<8) + res[2])<<8) + res[1])<<8) + res[0]) / CONVERT
        print "Startup frequency is %f MHz" % f
        return int(f * MEGA)  # In Hz

    def getXtal(self):
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = GET_XTAL_FREQ,
                                     buffer = 4,
                                     value=0,
                                     index=0,
                                     timeout=100)
        f = ((((((res[3]<<8) + res[2])<<8) + res[1])<<8) + res[0]) / CONVERT_8_24
        print "Crystal frequency is %f MHz" % f
        return int(f * MEGA)   # In Hz

    def getLoOffset(self):
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = GET_SUB_MULT,
                                     buffer = 8,
                                     value=0,
                                     index=0,
                                     timeout=100)
        sub = ((((((res[3]<<8) + res[2])<<8) + res[1])<<8) + res[0])
        if (sub & 0x80000000):  # Check int32 sign bit
            sub = sub - 0x100000000
        sub = sub / CONVERT_11_21
        sub = sub / MULTIPLIER
        print "LO offset is %f kHz" % (sub * 1000)
        return sub

    def getSoftrockVersion(self):
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = GET_VERSION,
                                     buffer = 2,
                                     value=0,
                                     index=0,
                                     timeout=100)
        major = res[1]
        minor = res[0]
        print "Softrock compatibility version is %d.%d" % (major, minor)
        return

    def getSvnVersion(self):
        try:
            res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                         request = GET_FIFI_EXTRA,
                                         buffer = 4,
                                         value=0,
                                         index=EXTRA_READ_SVN_VERSION,
                                         timeout=100)
            svn = (((((res[3]<<8) + res[2])<<8) + res[1])<<8) + res[0]
            print "FiFi-SDR firmware version (SVN) is %d" % svn
            return svn
        except usb.USBError:
            print "Cannot determine FiFi-SDR firmware version"
            return 0

    def OnButtonRfGain(self, event, name=None):
        if event is not None:
            win = event.GetEventObject()
            name = win.GetLabel()
        value = -1
        if name == '0 dB':
            value = 1
        if name == '-6 dB':
            value = 0
        if value >= 0:
            self.handle.controlMsg(requestType = HOST2DEVICE,
                                   request = SET_FIFI_EXTRA,
                                   buffer = self.pack1(int(value)),
                                   value=0,
                                   index=EXTRA_WRITE_PREAMP,
                                   timeout=100)
        return

    def OnButtonPTT(self, event):
        button = event.GetEventObject()
        if button.GetValue():
            self.ptt = 1
        else:
            self.ptt = 0
        res = self.handle.controlMsg(requestType = DEVICE2HOST,
                                     request = SET_PTT,
                                     buffer = 2,
                                     value = self.ptt,
                                     index = 0,
                                     timeout = 100)
        return



