Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C#

Using OpenTK/OpenAL to Develop Cross Platform DIS VOIP Application

Rate me:
Please Sign up or sign in to vote.
4.79/5 (8 votes)
15 Mar 2010BSD10 min read 44.7K   1.7K   24  
Application allows voice communications (VOIP) utilizing the Distributed Interactive Simulation protocol (IEEE 1278.1)
// Copyright (c) 2010, Peter Smith
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list 
// of conditions and the following disclaimer. 
//
// Redistributions in binary form must reproduce the above copyright notice, this 
// list of conditions and the following disclaimer in the documentation and/or 
// other materials provided with the distribution. 
//
// Neither the name of the  Author nor the names of its contributors may be used to 
// endorse or promote products derived from this software without specific prior 
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
// OF THE POSSIBILITY OF SUCH DAMAGE.

namespace PS
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Media;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;

    using OpenTK;
    using OpenTK.Audio;
    using OpenTK.Audio.OpenAL;

    public partial class RadioTestSuite : Form
    {
        #region Fields

        #region CODECS

        private LumiSoft.Net.Media.Codec.Audio.PCMU uLaw = new LumiSoft.Net.Media.Codec.Audio.PCMU();

        #endregion

        #region Audio Out

        //Create the audio out component
        AudioOUT.PlayAudio playAudio;

        #endregion

        #region Audio In

        //Create the audio in component
        AudioIN.Microphone microphone;

        //Thread used to poll microphone for data
        Thread MicrophoneCapturedDataThread = null;

        //Sample rate used to capture data per second
        private int audioSamplingRate = 8000;

        //Number of samples to collect (8000 samples per second / number of Samples = 64 ms of audio capture)
        private int numberOfSamples = 512;

        //Gain of microphone
        private float microphoneGain = 4.0f;

        //Used to export incoming voice data to wav format
        MemoryStream memoryStreamAudioSaveBuffer = new MemoryStream();

        #endregion

        #region Protocol Data Unit

        private const uint PDU_LENGTH_POSITION = 8;
        private const uint PDU_TYPE_POSITION = 2;
        private const uint PDU_VERSION_POSITION = 0;
        private const double DIS_TIMESTAMP_SCALE = ((double)0x7fffffff / 3600.0); // (2^31 -1) Maximum value / seconds in hour

        //Default to using endian type BIG
        private DISnet.DataStreamUtilities.EndianTypes.Endian endianType = DISnet.DataStreamUtilities.EndianTypes.Endian.BIG;

        PDUProcessorClass_Overrides pduReceive = new PDUProcessorClass_Overrides();
        DIS1998net.SignalPdu signalPDU = new DIS1998net.SignalPdu();
        DIS1998net.TransmitterPdu transmitterPDU = new DIS1998net.TransmitterPdu();
        DIS1998net.EntityID entityID;

        //Set intial state to off
        DIS1998net.TransmitterPdu.TransmitStateEnum currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOff;

        private List<SiteHostEntityRadioFrequency> siteHostEntityRadioFrequencyCollection = new List<SiteHostEntityRadioFrequency>();

        #region PDU Heartbeat Timer

        System.Timers.Timer timerTransmitter = new System.Timers.Timer(5000);

        #endregion

        #endregion

        #region Radio Features

        private ulong currentTransmittingFrequency = 0;
        private string currentTransmitterID = "";
        private bool isPTTActivated = false;
        private Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass> frequencySpeakerLocationTransmitterReceiverActive = new Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass>();
        private int numberOfRadios;
        private bool isLoopBackAudioEnabled = false;
        private enum ReceiveTransmitIndication { NotTransmitting, Transmitting, NotReceiving, Receiving };
        
        #endregion

        #region Socket

        SocketCommunications.BroadCastReceive receiveBroadCast;
        SocketCommunications.BroadCastSend sendData;
        Thread receiveSocketData = null;
        private string ipBroadCastAddressUsed;
        private string ipUnicastAddressUsed;

        private bool continueSendingMicrophoneData = false;
        private bool continueReceivingSocketData = true;

        private int defaultPort = 9737;
        private ulong transmitterPacketsReceived = 0;
        private ulong signalPacketsReceived = 0;

        #endregion

        #region Exercise Timers

        private enum TypeTimeStamp { Absolute, Relative };
        private Stopwatch exerciseTimer;
        private DateTime ExerciseStartTime;
        private Int64 ExerciseTimerStartValue = 0;

        #endregion

        object lockThis = new object();
        public delegate void InvokeDelegate();

        #endregion Fields

        #region Constructors

        public RadioTestSuite()
        {
            InitializeComponent();
            if (SocketCommunications.SocketHelper.IPAddressesAvailable.Count == 0)
            {
                MessageBox.Show("No Network Interface Cards found");
                return;
            }

            InitializeTimer();
            InitializeRadioCommunications();
            InitializeRadioReceiversTransmitters();
        }

        #endregion Constructors

        #region Properties

        public Threaded.TList<FrequencySpeakerLocationTransmitterReceiverActiveClass> FrequencySpeakerLocationTransmitterReceiverActive
        {
            get
            {
                return frequencySpeakerLocationTransmitterReceiverActive;
            }

            set
            {
                frequencySpeakerLocationTransmitterReceiverActive = value;
            }
        }

        #endregion Properties

        #region Methods

        private void InitializeRadioCommunications()
        {
            //Initialize socket for sending BroadCast messages
            sendData = new SocketCommunications.BroadCastSend();

            //Grabbing first IP address in list
            ipUnicastAddressUsed = SocketCommunications.SocketHelper.IPAddressesAvailable[0].IPAddress.ToString();

            //Grabbing second IP address in list (note that this list contains both the unicast IP then the broadcast IP).  Therefore the odd values
            //will be the broadcast address based upon the previous unicast address in the list
            ipBroadCastAddressUsed = SocketCommunications.SocketHelper.IPAddressesAvailable[0].BroadCastAddress.ToString();

            //Initializing the socket to broadcast over the selected IP address and a default port number
            sendData.StartBroadCastSend(ipBroadCastAddressUsed, defaultPort);

            //Using the unicast IP address to assign to the entityID (each entity ID should be unique therefore IP address used, so do not run
            //more than 1 instance of this app on the same machine
            entityID = GetEntityID(ipUnicastAddressUsed);

            //Assign default values to the Transmitter PDU, this PDU will be sent out to notify all other radios of its existence
            transmitterPDU.EntityId = entityID;
            transmitterPDU.RadioId = 1;
            transmitterPDU.ExerciseID = 1;

            //Start the heart beat, value set to 5 seconds
            timerTransmitter.Elapsed += new System.Timers.ElapsedEventHandler(timerTransmitter_Elapsed);
            timerTransmitter.Enabled = true;

            //Use some default settings to send out the actual voice data
            signalPDU.SampleRate = (uint)audioSamplingRate; //Default 8000 samples per second
            signalPDU.Samples = (short)numberOfSamples;  //number of samples in this packet 512 (with 8000 samples per second would give 64ms of audio)
            //Set up what type of encoding was used
            signalPDU.RadioSignalEncodingClass = DISnet.Enumerations.SignalPDUEnumerations.RadioSignalEncodingClassEnum.Encoded_Audio;
            signalPDU.RadioSignalEncodingType = DISnet.Enumerations.SignalPDUEnumerations.RadioSignalEncodingTypeEnum.Eight_Bit_MuLaw;

            //Set up socket to receive broadcast data from other radios
            receiveBroadCast = new SocketCommunications.BroadCastReceive(ipBroadCastAddressUsed, defaultPort);

            //Can be used to find out all of the capture devices on the system.
            //IList<string> audioCaptureDevices = AudioCapture.AvailableDevices;
            //foreach (var item in audioCaptureDevices)
            //{
            //    string availableCaptureDevices = item.ToString();
            //}

            //Initialize the Microphone using the default microphone
            StartMicrophone(audioSamplingRate, microphoneGain, AudioCapture.DefaultDevice, ALFormat.Mono16, numberOfSamples * 2);

            //Start the process to send out microphone data through the socket
            StartThreadSendingMicrophoneSoundToSignalPDU();

            //Initialize the audio player
            playAudio = new AudioOUT.PlayAudio();

            toolStripStatusLabel_IPPort.Text = ipBroadCastAddressUsed + ":" + defaultPort.ToString();
            //Start receiving transmitter and signal pdus from other radios
            StartThreadToReceiveSocketData();
        }

        private void InitializeRadioReceiversTransmitters()
        {
            //Initialize number of radios
            numberOfRadios = 0;

            //This will count the number of radios found on the Form
            FindControl(this.Controls);
        }

        /// <summary>
        /// Starts the exercise Timer
        /// </summary>
        private void InitializeTimer()
        {
            exerciseTimer = new Stopwatch();
            exerciseTimer.Reset();
            exerciseTimer.Start();
            ExerciseTimerStartValue = exerciseTimer.ElapsedTicks;
        }

        /// <summary>
        /// Sets whether or not to loop the users recording back into their speaker
        /// </summary>
        private void checkBox_LoopBackAudioCheck_CheckedChanged(object sender, EventArgs e)
        {
            if (this.checkBox_LoopBackAudioCheck.Checked == true)
                isLoopBackAudioEnabled = true;
            else
                isLoopBackAudioEnabled = false;
        }

        /// <summary>
        /// Used to ensure that all started threads and processes are shut down correctly
        /// </summary>
        private void CompleteTest_FormClosing(object sender, FormClosingEventArgs e)
        {
            continueSendingMicrophoneData = false;

            continueReceivingSocketData = false;
            timerTransmitter.Enabled = false;

            FrequencySpeakerLocationTransmitterReceiverActive.Clear();
            siteHostEntityRadioFrequencyCollection.Clear();

            if (microphone != null)
            {
                microphone.StopRecording();
            }

            if (playAudio != null)
            {
                playAudio.StopPlayBack();
                playAudio = null;
            }

            if (receiveBroadCast != null)
            {
                receiveBroadCast.ShutDownSocket();
                receiveBroadCast = null;
            }

            if (sendData != null)
            {
                sendData.ShutdownSocket();
                sendData = null;
            }
        }

        /// <summary>
        /// Used to find a Radio control on the main form.  Used to initialize the Radios found by providing
        /// a unique radio tag which will be used when changing frequencys or speaker location.
        /// </summary>
        /// <param name="cntrl">Control collection from the Form</param>
        private void FindControl(Control.ControlCollection cntrl)
        {
            foreach (Control c in cntrl)
            {
                if (c.GetType() == typeof(Radio))
                {
                    FrequencySpeakerLocationTransmitterReceiverActiveClass tempRadioInfo = new FrequencySpeakerLocationTransmitterReceiverActiveClass();
                    Radio r = c as Radio;
                    r.Tag = numberOfRadios;
                    tempRadioInfo.UniqueID = r.Name;
                    frequencySpeakerLocationTransmitterReceiverActive.Add(tempRadioInfo);
                    numberOfRadios++;
                }
                else if (c.Controls.Count > 0)
                    FindControl(c.Controls);
            }
        }

        /// <summary>
        /// Determines if the frequency is suppose to be played back.
        /// </summary>
        /// <param name="frequency">Frequency to check</param>
        /// <returns>True if frequency should be played back</returns>
        private bool FrequencyOnPlayBackList(ulong frequency)
        {
            foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
            {
                //if freqency exists and it was selected to be a receiver or a transmitter than play back sound
                if (item.Frequency == frequency && (item.ActiveReceiver == true || item.ActiveTransmitter == true))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Method used to set unique Radio ID based upon the IP address of the system.  Note that any method could be used as 
        /// long as the Radio is unique on the Network
        /// </summary>
        /// <param name="SimulationIPAddress">IP address of the system</param>
        /// <returns>Entity ID</returns>
        private DIS1998net.EntityID GetEntityID(string SimulationIPAddress)
        {
            string receiveAddress = SimulationIPAddress; //Just defaulting to first one in list
            int lastOctectStart = receiveAddress.LastIndexOf('.') + 1;
            string lastOctect = receiveAddress.Substring(lastOctectStart, (receiveAddress.Length - lastOctectStart));
            return new DIS1998net.EntityID(1, 2, System.Convert.ToUInt16(lastOctect));
        }

        /// <summary>
        /// Intializes the microphone
        /// </summary>
        /// <param name="samplingRate">Sample rate of recording</param>
        /// <param name="microphoneGain">Software gain of microphone</param>
        /// <param name="deviceCaptureName">System name of device used to capture audio</param>
        /// <param name="format">Format of recorded data</param>
        /// <param name="bufferSize">Size of buffer</param>
        private void StartMicrophone(int samplingRate, float microphoneGain, string deviceCaptureName, ALFormat format, int bufferSize)
        {
            microphone = new AudioIN.Microphone(samplingRate, microphoneGain, deviceCaptureName, format, bufferSize);

            if (microphone.isMicrophoneValid == false)
            {
                MessageBox.Show("ERROR SETTING UP MICROPHONE");
            }
            else
            {
                //Start accepting input from the microphone
                microphone.StartRecording();

                //Set flag to send captured data out to Signal PDU
                continueSendingMicrophoneData = true;
            }
        }

        /// <summary>
        /// Starts thread that will continue to poll microphone and send data out over Signal PDU
        /// </summary>
        private void StartThreadSendingMicrophoneSoundToSignalPDU()
        {
            if (MicrophoneCapturedDataThread == null || MicrophoneCapturedDataThread.IsAlive == false)
            {
                continueSendingMicrophoneData = true;
                MicrophoneCapturedDataThread = new Thread(OutputMicrophoneSoundToSignalPDU);
                MicrophoneCapturedDataThread.Start();
            }
        }

        /// <summary>
        /// Microphone data will be sent out via a Signal PDU
        /// </summary>
        private void OutputMicrophoneSoundToSignalPDU()
        {
            while (continueSendingMicrophoneData)
            {
                if (microphone.MicrophoneData.Count > 0)
                {
                    //Remove data from the Queue
                    byte[] raw = microphone.MicrophoneData.Dequeue();

                    //If PTT is true then data will be sent out on the PDU
                    if (isPTTActivated == true)
                    {
                        byte[] encodedData = uLaw.Encode(raw, 0, raw.Length);
                        raw = uLaw.Decode(encodedData, 0, encodedData.Length);
                        signalPDU.ExerciseID = transmitterPDU.ExerciseID;
                        signalPDU.EntityId = transmitterPDU.EntityId;
                        signalPDU.RadioId = transmitterPDU.RadioId;
                        signalPDU.Data = encodedData;
                        signalPDU.Samples = System.Convert.ToInt16(encodedData.Length);
                        signalPDU.Timestamp = TimeStamp(TypeTimeStamp.Absolute);
                        DISnet.DataStreamUtilities.DataOutputStream ds2 = new DISnet.DataStreamUtilities.DataOutputStream(endianType);
                        signalPDU.marshalAutoLengthSet(ds2);
                        sendData.BroadcastMessage(ds2.ConvertToBytes());
                    }
                }

                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// Threaded method that will continue to poll socket for data
        /// </summary>
        private void RetreiveDataFromSocket()
        {
            byte pdu_type;
            byte pdu_version;

            //Continue to process data received until false
            while (continueReceivingSocketData == true)
            {
                //Using queued collection to determine if data has arrived
                if (receiveBroadCast.pduQueue.Count > 0)
                {
                    //Process any PDUs (note that a collection was used as multiple pdu's can be sent in one packet)
                    List<byte[]> pdus = pduReceive.ProcessRawPDU(receiveBroadCast.pduQueue.Dequeue(), endianType);

                    int countPDU = 0;
                    int pduCount = pdus.Count;

                    while (countPDU < pduCount && continueReceivingSocketData == true)
                    {
                        byte[] objPdu = pdus[countPDU];
                        pdu_type = objPdu[PDU_TYPE_POSITION]; //get pdu type

                        pdu_version = objPdu[PDU_VERSION_POSITION];//what version (currently not processing anything but DIS 1998)

                        //Cast as radio pdu, as receive socket method will throw out all other types of PDUs
                        DIS1998net.RadioCommunicationsFamilyPdu pduReceived = pduReceive.ConvertByteArrayToPDU1998(pdu_type, objPdu, endianType) as DIS1998net.RadioCommunicationsFamilyPdu;

                        SiteHostEntityRadioFrequency siteHostEntityRadioFrequency = new SiteHostEntityRadioFrequency(pduReceived.EntityId.Site, pduReceived.EntityId.Application, pduReceived.EntityId.Entity, pduReceived.RadioId, 0);

                        if (pduReceived.EntityId != entityID || (pduReceived.EntityId == entityID && this.isLoopBackAudioEnabled == true))
                        {
                            //Transmitter which contains the frequency of the signal pdu
                            if (pdu_type == 25)
                            {

                                //Update transmitter packets received
                                UpdatedTransmitterPacketsReceived();

                                DIS1998net.TransmitterPdu transmitterPDU_Received = (DIS1998net.TransmitterPdu)pduReceived;

                                //Assign the frequency from the Transmitter PDU just received.
                                siteHostEntityRadioFrequency.frequency = transmitterPDU_Received.Frequency;

                                //Remove the frequency from the collection as it will be added back later if it is transmitting
                                siteHostEntityRadioFrequencyCollection.Remove(siteHostEntityRadioFrequencyCollection.Find(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site));

                                //If transmitter is transmitting then add to collection
                                if (transmitterPDU_Received.TransmitStateEnumeration == DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnTransmitting)
                                {
                                    siteHostEntityRadioFrequencyCollection.Add(siteHostEntityRadioFrequency);
                                }

                                foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
                                {
                                    if (siteHostEntityRadioFrequencyCollection.Exists(i => i.frequency == item.Frequency) == true)
                                    {
                                        ChangeRadioRecievingColorIndicator(item.UniqueID,true);
                                    }
                                    else
                                        ChangeRadioRecievingColorIndicator(item.UniqueID, false);
                                }
                            }
                            

                            //is it a signal PDU?
                            if (pdu_type == 26)
                            {
                                //Update signal packets received
                                UpdatedSignalPacketsReceived();

                                DIS1998net.SignalPdu signalPDU = (DIS1998net.SignalPdu)pduReceived;

                                //Does the current signal pdu match one in the transmitter collection
                                if (siteHostEntityRadioFrequencyCollection.Exists(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site) == true)
                                {
                                    //Retrieve the saved frequency
                                    SiteHostEntityRadioFrequency storedSitehostEntityRadioFrequency = siteHostEntityRadioFrequencyCollection.Find(i => i.application == siteHostEntityRadioFrequency.application && i.entity == siteHostEntityRadioFrequency.entity && i.radioID == siteHostEntityRadioFrequency.radioID && i.site == siteHostEntityRadioFrequency.site);

                                    //Transmitter was transmitting at this frequency, need to check to see if it is one that we will playback
                                    siteHostEntityRadioFrequency.frequency = storedSitehostEntityRadioFrequency.frequency;

                                    if (FrequencyOnPlayBackList(storedSitehostEntityRadioFrequency.frequency) == true)
                                    {
                                        //Need to retrieve the speaker location that matches the frequency
                                        FrequencySpeakerLocationTransmitterReceiverActiveClass retrievedFreqSpeakerTransmitterReceiver = FrequencySpeakerLocationTransmitterReceiverActive.Find(i => i.Frequency == storedSitehostEntityRadioFrequency.frequency);

                                        //Decode the data, only implemented uLaw 
                                        byte[] unEncodedData = uLaw.Decode(((DIS1998net.SignalPdu)pduReceived).Data, 0, ((DIS1998net.SignalPdu)pduReceived).Data.Length);

                                        //Used to save to a stream
                                        //ms.Seek(ms.Length, SeekOrigin.Begin);
                                        //ms.Write(encodedData, 0, encodedData.Length);

                                        //Play back unencoded data
                                        playAudio.PlayBackAudio(unEncodedData, ALFormat.Mono16, (int)signalPDU.SampleRate, retrievedFreqSpeakerTransmitterReceiver.SpeakerLocation);
                                    }
                                }
                            }
                        }

                        countPDU++;

                        //Give GUI time to do things
                        Thread.Sleep(1);
                    }
                }
                //Give GUI time to do things
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// Radio button pressed, pop up form to assign new frequency and speaker locations 
        /// </summary>
        private void radio_RadioButtonPressed(object sender, EventArgs e)
        {
            //Get the Radio that was clicked
            Radio radioButton = sender as Radio;

            //Find the unique Tag
            short radioID = System.Convert.ToInt16(radioButton.Tag);

            //Get the Radio Name
            string uniqueRadioIDName = radioButton.Name;

            //Find out the current Frequency and speaker location
            FrequencySpeakerLocationTransmitterReceiverActiveClass tempFreqSpeakerTransReceiverActive = FrequencySpeakerLocationTransmitterReceiverActive.Find(i => i.UniqueID == uniqueRadioIDName);

            //Assign the current values to the Radio Form
            RadioInterfaceForm radioInterfaceForm = new RadioInterfaceForm(tempFreqSpeakerTransReceiverActive);

            //Center the form
            radioInterfaceForm.Location = CenterModalForm(radioInterfaceForm.Size);

            //Show the form
            if (radioInterfaceForm.ShowDialog() == DialogResult.OK)
            {
                bool isFrequencyValid = true;

                //Check to see frequency already assigned to another Radio
                foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
                {
                    if (item.UniqueID != uniqueRadioIDName)
                    {
                        if (radioInterfaceForm.FrequencyAndSpeakerLocation.Frequency == item.Frequency && radioInterfaceForm.Frequency != 0)
                        {
                            isFrequencyValid = false;
                            MessageBox.Show("Frequency already assigned");
                            break;
                        }
                    }
                }

                if (isFrequencyValid)
                {
                    if (radioInterfaceForm.FrequencyAndSpeakerLocation.ActiveTransmitter == true)
                    {
                        //Disable from sending out heartbeat until new frequency assigned
                        this.timerTransmitter.Enabled = false;

                        foreach (FrequencySpeakerLocationTransmitterReceiverActiveClass item in FrequencySpeakerLocationTransmitterReceiverActive)
                        {
                            //Turn off all other transmitters, only one allowed
                            if (item.UniqueID != uniqueRadioIDName)
                            {
                                if (item.ActiveTransmitter == true)
                                {
                                    //turn off the one that was a transmitter
                                    item.ActiveTransmitter = false;
                                    //Set it to just be a receiver
                                    item.ActiveReceiver = true;
                                    //Set correct color indicator
                                    ChangeRadioColorIndicator(item.UniqueID, Radio.RadioType.Receiver);
                                }
                            }
                        }

                        //Assign new frequency to transmitter
                        currentTransmittingFrequency = radioInterfaceForm.FrequencyAndSpeakerLocation.Frequency;

                        //Change background shadow to indicate this is a transmitter
                        ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Transmitter);

                        //Assign the Unique Radio ID that is currently transmitting, used to change background color
                        currentTransmitterID = uniqueRadioIDName;

                        //Set current state to not transmitting
                        currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnNotTransmitting;

                        //Transmit using new frequency then re-enable heartbeat
                        TransmitPDU();
                        this.timerTransmitter.Enabled = true;
                    }
                    else //ActiveTransmitter can be false, ActiveReceiver can be true or false
                    {
                        //If this radio was the transmitter, it has now been turned off so set currentTransmittingFrequency to 0
                        if (tempFreqSpeakerTransReceiverActive.ActiveTransmitter == true)
                        {
                            //Disable from sending out heartbeat until new frequency assigned
                            this.timerTransmitter.Enabled = false;

                            //Turn off Transmitter flag
                            tempFreqSpeakerTransReceiverActive.ActiveTransmitter = false;

                            //As there are no transmitters at this moment set to default freq 0
                            currentTransmittingFrequency = 0;

                            //Remove indication of active Transmitter from button
                            ChangeRadioColorIndicator(currentTransmitterID, Radio.RadioType.Neither);

                            //Remove TransmitterID
                            currentTransmitterID = "";

                            //Turn state to off, not sure if this is really valid as other radios can be receiving
                            currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOff;

                            //Transmit that we are off (not transmitting)
                            TransmitPDU();

                            //Re-enable heartbeat timer
                            this.timerTransmitter.Enabled = true;
                        }

                        if (radioInterfaceForm.FrequencyAndSpeakerLocation.ActiveReceiver == true)
                        {
                            //Set background shadow color to indicate Receiver
                            ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Receiver);
                        }
                        else
                        {
                            //Remove shadow as neither transmitter or receiver selected
                            ChangeRadioColorIndicator(uniqueRadioIDName, Radio.RadioType.Neither);
                        }
                    }
                   
                    //Add the changes back in
                    tempFreqSpeakerTransReceiverActive = radioInterfaceForm.FrequencyAndSpeakerLocation;

                    FrequencySpeakerLocationTransmitterReceiverActive[radioID] = tempFreqSpeakerTransReceiverActive;

                    //If frequency was 0 then clear out text
                    radioButton.Text = radioInterfaceForm.Frequency > 0 ? radioInterfaceForm.Frequency.ToString() : "";
                }
            }

            radioInterfaceForm.Close();
        }

        /// <summary>
        /// Transmit audio over the frequency assigned to be the transmitter
        /// </summary>
        private void button_Transmit_MouseDown(object sender, MouseEventArgs e)
        {
            PTTActivated();
        }

        private void PTTActivated()
        {
            if (this.currentTransmittingFrequency != 0)
            {
                ChangeTransmitIndicator(this.currentTransmitterID, ReceiveTransmitIndication.Transmitting);
                this.currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnTransmitting;
                this.TransmitPDU();

                this.isPTTActivated = true;
            }
        }

        /// <summary>
        /// User released Transmit button, check to see if Transmit Indicators need to be switched
        /// </summary>
        private void button_Transmit_MouseUp(object sender, MouseEventArgs e)
        {
            PTTDeactivated();
        }

        private void PTTDeactivated()
        {
            if (this.currentTransmittingFrequency != 0)
            {
                ChangeTransmitIndicator(this.currentTransmitterID, ReceiveTransmitIndication.NotTransmitting);

                this.isPTTActivated = false;
                this.currentTransmittingState = DIS1998net.TransmitterPdu.TransmitStateEnum.TransmitterOnNotTransmitting;
                this.TransmitPDU();
            }
        }

        private void ChangeRadioColorIndicator(string UniqueRadioID, Radio.RadioType radioType)
        {
            Radio r = this.Controls.Find(UniqueRadioID, true)[0] as Radio;

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    r.SetRadioShadowType(radioType);
                }));
            }
            else
                r.SetRadioShadowType(radioType);
        }

        private void ChangeRadioRecievingColorIndicator(string uniqueRadioID, bool receiving)
        {
            Radio r = this.Controls.Find(uniqueRadioID, true)[0] as Radio;

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    r.ChangeRadioReceivingSignal(receiving);
                }));
            }
            else
                r.ChangeRadioReceivingSignal(receiving);
        }

        /// <summary>
        /// Changes the indicator used to show that data is being transmitted
        /// </summary>
        /// <param name="UniqueRadioID">Unique ID of radio</param>
        /// <param name="receiveTransmitIndication">Type of transmission being sent</param>
        private void ChangeTransmitIndicator(string UniqueRadioID, ReceiveTransmitIndication receiveTransmitIndication)
        {
            Radio r = this.Controls.Find(UniqueRadioID, true)[0] as Radio;

            switch (receiveTransmitIndication)
            {
                case ReceiveTransmitIndication.NotTransmitting:
                    if (this.InvokeRequired)
                    {
                        this.BeginInvoke(new MethodInvoker(delegate()
                        {
                            r.RadioStoppedTransmitting();
                        }));
                    }
                    else
                        r.RadioStoppedTransmitting();
                    break;
                case ReceiveTransmitIndication.Transmitting:
                    if (this.InvokeRequired)
                    {
                        this.BeginInvoke(new MethodInvoker(delegate()
                        {
                            r.RadioTransmitting();
                        }));
                    }
                    else
                        r.RadioTransmitting();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Used to update transmitter packet count
        /// </summary>
        private void UpdatedTransmitterPacketsReceived()
        {
            transmitterPacketsReceived++;

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    toolStripStatusLabel_TransmitterPackets.Text = transmitterPacketsReceived.ToString();
                }));
            }
            else
                toolStripStatusLabel_TransmitterPackets.Text = transmitterPacketsReceived.ToString();
        }

        /// <summary>
        /// Used to update signal packet count
        /// </summary>
        private void UpdatedSignalPacketsReceived()
        {
            signalPacketsReceived++;

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    toolStripStatusLabel_SignalPakcets.Text = signalPacketsReceived.ToString();
                }));
            }
            else
                toolStripStatusLabel_SignalPakcets.Text = signalPacketsReceived.ToString();
        }

        /// <summary>
        /// Center a form
        /// </summary>
        /// <param name="childForm">Form to be centered</param>
        /// <returns>X, Y coordinates</returns>
        private Point CenterModalForm(Size childForm)
        {
            Point newLocation = new Point();
            int xLoc = this.Location.X;
            int yLoc = this.Location.Y;
            Size s = this.Size;

            newLocation.X = (xLoc + s.Width / 2) - childForm.Width / 2;
            newLocation.Y = (yLoc + s.Height / 2) - childForm.Height / 2;

            return newLocation;
        }

        /// <summary>
        /// Type of typestamp to apply to outgoing PDUs
        /// </summary>
        /// <param name="typeTimeStamp">Type of timestamp to use</param>
        /// <returns>Integer value of the resulting timestamp</returns>
        private UInt32 TimeStamp(TypeTimeStamp typeTimeStamp)
        {
            UInt32 timeStamp = 0;

            //ExerciseStartTime
            switch (typeTimeStamp)
            {
                case TypeTimeStamp.Absolute:
                    timeStamp = AbsoluteTimeStamp(exerciseTimer.ElapsedTicks);
                    break;
                case TypeTimeStamp.Relative:
                    timeStamp = RelativeTimeStamp(exerciseTimer.ElapsedTicks);
                    break;
                default:
                    break;
            }

            return timeStamp;
        }


        /// <summary>
        /// Creates an Absolute Time Stamp
        /// </summary>
        /// <param name="timeTicks">Number of ticks based upon the start of the exercise in UTC</param>
        /// Note:  Having read the IEEE 1278.1 standard I am not sure this is implemented correctly</param>
        /// <returns>Integer value of the resulting timestamp</returns>
        private UInt32 AbsoluteTimeStamp(long timeTicks)
        {
            UInt32 timeStamp = 0;

            //Number of seconds past the hour according to the IEEE standard for absolute stamp
            DateTime time = this.ExerciseStartTime.AddTicks(timeTicks - ExerciseTimerStartValue);

            //Number of seconds past the hour according to the IEEE standard for relative stamp
            double totalSeconds = time.Minute * 60.0 + time.Second + time.Millisecond / 1000.0;

            timeStamp = System.Convert.ToUInt32(totalSeconds * DIS_TIMESTAMP_SCALE);

            timeStamp = (timeStamp << 1) | 1;//Shifting by 1 bit then adding 1 in the LSB represents Absolute

            return timeStamp;
        }

        /// <summary>
        /// Create a relative Time Stamp
        /// </summary>
        /// <param name="timeTicks">Number of ticks based upon the start of the exercise that all participants agree upon.
        /// Note:  Having read the IEEE 1278.1 standard I am not sure this is implemented correctly</param>
        /// <returns></returns>
        private UInt32 RelativeTimeStamp(long timeTicks)
        {
            UInt32 timeStamp = 0;

            //TimeSpan time = TimeSpan.FromTicks(timeTicks);
            //Int64 timeElapseInTenthsOfMilliseconds = (timeTicks * 10000) / timer.Frequency;

            DateTime time = this.ExerciseStartTime.AddTicks(timeTicks - ExerciseTimerStartValue);

            //Number of seconds past the hour according to the IEEE standard for relative stamp
            double totalSeconds = time.Minute * 60.0 + time.Second + time.Millisecond / 1000.0;

            timeStamp = System.Convert.ToUInt32(totalSeconds * DIS_TIMESTAMP_SCALE * 2.0); //Multiplying by 2 to shift bits to the left a 0 in the LSB represents Relative

            return timeStamp;
        }

        /// <summary>
        /// Used to save audio data into a wave compatible format
        /// </summary>
        /// <param name="channels">Number of channels (mono or stereo)</param>
        /// <param name="samplerate">Sample rate of audio data</param>
        /// <param name="BitsPerSample">Number of bits per sample</param>
        private void RIFFHeader(short channels, int samplerate, short BitsPerSample)
        {
            //For the default state: RIFFHeader(1, 8000, 16);

            int fileSize = 36 + (int)memoryStreamAudioSaveBuffer.Length;

            FileStream fs = new FileStream("AudioReceived.wav", FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);
            fs.Position = 0;
            bw.Write(new char[4] { 'R', 'I', 'F', 'F' });

            bw.Write(fileSize);

            bw.Write(new char[8] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });

            bw.Write((int)16);

            bw.Write((short)1);
            bw.Write(channels);

            bw.Write(samplerate);

            bw.Write((int)(samplerate * ((BitsPerSample * channels) / 8)));

            bw.Write((short)((BitsPerSample * channels) / 8));

            bw.Write(BitsPerSample);

            bw.Write(new char[4] { 'd', 'a', 't', 'a' });
            bw.Write((int)memoryStreamAudioSaveBuffer.Length);
            bw.Write(memoryStreamAudioSaveBuffer.ToArray());
            bw.Close();
            fs.Close();
        }

        /// <summary>
        /// Start thread that will receive socket data
        /// </summary>
        private void StartThreadToReceiveSocketData()
        {
            //allow processing of socket data
            continueReceivingSocketData = true;
            //Start the thread
            receiveSocketData = new Thread(RetreiveDataFromSocket);
            receiveSocketData.Start();
        }

        /// <summary>
        /// Heart beat timer used to send updates about the transmitter on this Radio
        /// </summary>
        private void timerTransmitter_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            TransmitPDU();
        }

        /// <summary>
        /// Send out a Transmitter PDU
        /// </summary>
        private void TransmitPDU()
        {
            transmitterPDU.Frequency = currentTransmittingFrequency;
            transmitterPDU.TransmitStateEnumeration = currentTransmittingState;
            transmitterPDU.Timestamp = TimeStamp(TypeTimeStamp.Absolute);
            DISnet.DataStreamUtilities.DataOutputStream ds = new DISnet.DataStreamUtilities.DataOutputStream(endianType);
            transmitterPDU.marshalAutoLengthSet(ds);
            this.sendData.BroadcastMessage(ds.ConvertToBytes());
        }

        #endregion Methods
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions