//-------------------------------------------------------------------------------------
// SID Monitor - Utility for Sudden Ionospheric Disturbances Monitoring Stations
// Copyright (C) 2006 - Lionel Loudet
//
// 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.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//-------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.Threading;


namespace SID_monitor
{
    /// <summary>
    /// 
    /// Serial Port ADC converter
    /// 
    /// Adapted from MAXIM Application note 827
    /// http://www.maxim-ic.com/appnotes.cfm/appnote_number/827
    /// "12 BIT A/D ON PC SERIAL PORT AND DRAWS MICROPOWER"
    /// Wettroth  12/5/94,	Maxim Integrated Products- Sunnyvale
    /// 
    /// </summary>
    public class SerialADC
    {
        TimeSpan ClockHoldTime = TimeSpan.FromTicks((long)( 0.01 * TimeSpan.TicksPerMillisecond ));    // 10µs clock high or low hold time -- min 0.125µs
        TimeSpan TimeoutMAX187 = TimeSpan.FromTicks((long)( 0.05 * TimeSpan.TicksPerMillisecond )); // 50µs MAX187 timeout -- min 8.5µs
        TimeSpan CSPulseWidth = TimeSpan.FromTicks((long)( 0.01 * TimeSpan.TicksPerMillisecond )); // 10µs MAX187 /CS pulse width -- min 0.5µs

        private const int ThreadTimeout = 100;  // 100ms timeout for completion of ADC thread

        private SerialPort port = null;
        private UInt32 ad_value;
        private String errorMsg = String.Empty;

        public SerialADC(string port_name)
        {
            for ( int i = 0; i < SerialPort.GetPortNames().Length; i++ ) // we check if thr port_name is in the list of valid ports
            {
                if ( port_name == SerialPort.GetPortNames()[i] )
                {

                    try
                    {
                        port = new SerialPort(port_name);
                        break;
                    }
                    catch ( Exception ex )
                    {
                        Program.MainForm.outputTextBoxDockablePanel.AddOutputTextErrorMessage("\n*** Cannot access serial port \"" + port_name + "\"\n*** " + ex.ToString().Split('\n')[0] + "\n");
                    }

                }
            }
            if ( port == null )
            {
                Program.MainForm.outputTextBoxDockablePanel.AddOutputTextErrorMessage("\n*** Cannot access serial port \"" + port_name + "\"\n");
            }

        }

        public UInt32 GetADC()
        {
            if ( port != null )
            {
                try
                {
                    Thread currentGetADCThread = new Thread(new ThreadStart(GetADCThread));
                    currentGetADCThread.Priority = ThreadPriority.AboveNormal;
                    currentGetADCThread.Start();
                    Thread.Sleep(0);

                    if ( currentGetADCThread.Join(ThreadTimeout) == false )
                    {
                        Program.MainForm.outputTextBoxDockablePanel.AddOutputTextWarningMessage("Timeout exceeded for ADC response\n");
                        return 0;
                    }

                    if ( !String.IsNullOrEmpty(errorMsg) )
                    {
                        Program.MainForm.outputTextBoxDockablePanel.AddOutputTextWarningMessage(errorMsg);
                        return 0;
                    }
                    return ad_value;
                }
                catch  ( Exception ex )
                {
                    Program.MainForm.outputTextBoxDockablePanel.AddOutputTextRRDToolErrorMessage(ex.ToString().Split('\n')[0] + "\n");
                    return 0;
                }
            }
            else
            {
                Program.MainForm.outputTextBoxDockablePanel.AddOutputTextErrorMessage("\n*** Serial port not defined.\n");
                return 0;
            }
        }

        public void GetADCThread()
        {
            ad_value = 0;
            errorMsg = String.Empty;

            try
            {
                port.Open();

                // unsets RTS (RTS=-5V; /CS = high) to deselect MAX187
                port.RtsEnable = false;
                Thread.Sleep(CSPulseWidth);

                // sets DTR (DTR=+5V; SCLK = low)
                port.DtrEnable = true;

                // sets RTS (RTS=+5V; /CS = low) to select MAX187
                port.RtsEnable = true;
                Thread.Sleep(TimeoutMAX187);   // wait convert complete

                if (port.DsrHolding == true)  // conversion not complete -- DOUT should be high (DSR -5V=false)
                {
                    errorMsg += "ADC converter non responding.\n";
                }

                // clock in 12 bits to ad_value
                for (int i = 1; i <= 12; i++)
                {
                    Thread.Sleep(ClockHoldTime);
                    // unsets DTR (DTR=-5V; SCLK = high)
                    port.DtrEnable = false;       /* clock high */
                    Thread.Sleep(ClockHoldTime);

                    // sets DTR (DTR=+5V; SCLK = low)
                    port.DtrEnable = true;        /* clock low */
                    Thread.Sleep(ClockHoldTime);

                    // get a bit and put in place
                    if (port.DsrHolding == false) // DOUT is high (DSR -5V=false)
                    {
                        ad_value += 1;
                    }
                    // shift left one
                    ad_value = ad_value << 1;
                }

                // a 13th clock
                // unsets DTR (DTR=-5V; SCLK = high)
                port.DtrEnable = false;       /* clock high */
                Thread.Sleep(ClockHoldTime);
                // sets DTR (DTR=+5V; SCLK = low)
                port.DtrEnable = true;        /* clock low */
                Thread.Sleep(ClockHoldTime);


                // unsets RTS (RTS=-5V; /CS = high) to deselect MAX187
                port.RtsEnable = false;
                Thread.Sleep(CSPulseWidth);

                // unsets DTR (DTR=-5V; SCLK = high)
                port.DtrEnable = false;

                ad_value = ad_value >> 1;   // fixup

            }
            catch (Exception ex)
            {
                errorMsg += "\n*** Cannot acquire data." + ex.ToString().Split('\n')[0] + "\n";
            }
            finally
            {
                if ((port != null) && (port.IsOpen))
                {
                    port.Close();
                }
            }
        }



        #region Properties
        public bool IsOpened
        {
            get
            {
                return port.IsOpen;
            }
        }

        public string PortName
        {
            get
            {
                return port.PortName;
            }
        }
        #endregion

    }


}
