Пример #1
0
		public override void addValues (GVector gv)
		{
			GVector reading=gv;
		    if (!firstcall)// _lastReading != null)
            {
                if (!_shaking && CheckForShake(_lastReading, reading, ShakeThreshold) && _shakeCount >= 1)
                {
                    //We are shaking
                    _shaking = true;
                    _shakeCount = 0;
					OnShakeDetected(gv);
                    
                }
                else if (CheckForShake(_lastReading, reading, ShakeThreshold))
                {
                    _shakeCount++;
                }
                else if (!CheckForShake(_lastReading, reading, 0.2))
                {
                    _shakeCount = 0;
                    _shaking = false;
                }
            }
            _lastReading = reading;
			firstcall=false;

		}
Пример #2
0
        public override void addValues(GVector gv)
        {
            GVector reading = gv;

            if (!firstcall)        // _lastReading != null)
            {
                if (!_shaking && CheckForShake(_lastReading, reading, ShakeThreshold) && _shakeCount >= 1)
                {
                    //We are shaking
                    _shaking    = true;
                    _shakeCount = 0;
                    OnShakeDetected(gv);
                }
                else if (CheckForShake(_lastReading, reading, ShakeThreshold))
                {
                    _shakeCount++;
                }
                else if (!CheckForShake(_lastReading, reading, 0.2))
                {
                    _shakeCount = 0;
                    _shaking    = false;
                }
            }
            _lastReading = reading;
            firstcall    = false;
        }
Пример #3
0
        public override void addValues(GVector gv)
        {
            //use new GVector 16 segmented direction
            GVector.Direction direction = gv.direction;

            double accel = gv.Length;// Math.Sqrt(e.X * e.X + e.Y * e.Y);// + e.Z*e.Z);

            //Does the currenet acceleration vector meet the minimum magnitude that we
            //care about?
            if (accel > MinimumAccelerationMagnitudeSquared)
            {
                //If the shake detected is in the same direction as the last one then ignore it
                //if ((direction & _shakeRecordList[_shakeRecordIndex].ShakeDirection) != GVector.Direction.None)// Direction.None)
                if ((direction == _shakeRecordList[_shakeRecordIndex].ShakeDirection))
                {
                    return;
                }
                ShakeRecord record = new ShakeRecord();
                record.EventTime      = DateTime.Now;
                record.ShakeDirection = direction;
                _shakeRecordIndex     = (_shakeRecordIndex + 1) % _minimumShakes;
                _shakeRecordList[_shakeRecordIndex] = record;

                CheckForShakes(gv);
            }
        }
Пример #4
0
        public override void addValues(GVector gv)
        {
            //private static int SHAKE_THRESHOLD = 800;

            long lastUpdate = 0;
            long curTime = System.DateTime.Now.Millisecond;
            // only allow one update every 100ms.
            if ((curTime - lastUpdate) > 100)
            {
                long diffTime = (curTime - lastUpdate);
                lastUpdate = curTime;

                double speed = Math.Abs(gv.X + gv.Y + gv.Z - last_x - last_y - last_z) / diffTime * 10000;

                //adjust for CN50
                speed = speed / 10;
                if (speed > _shakeTreshold)
                {
                    this.Logger("Shake detected");
                    this.OnShakeDetected(gv);
                }
                else
                {
                    //System.Diagnostics.Debug.WriteLine("sensor : shake speed: " + speed.ToString());
                }
                gvLast = gv;
                last_x = gv.X;
                last_y = gv.Y;
                last_z = gv.Z;
            }
            else
                this.Logger("sensor : timespan to low ");
        }
Пример #5
0
        public long TicksDiff(GVector gvNew)
        {
            GVector gvOld = this;
            long    diff  = gvNew.Ticks - gvOld.Ticks;

            //one tick is 100 nano seconds
            return(diff);
        }
Пример #6
0
        public GVector Scale(double scale)
        {
            GVector ret = this;

            ret.myX *= scale;
            ret.myY *= scale;
            ret.myZ *= scale;
            return(ret);
        }
Пример #7
0
 private void CheckForShakes(GVector gv)
 {
     int startIndex = (_shakeRecordIndex - 1);
     if (startIndex < 0) startIndex = _minimumShakes - 1;
     int endIndex = _shakeRecordIndex;
     if ((_shakeRecordList[endIndex].EventTime.Subtract(_shakeRecordList[startIndex].EventTime)) <= MinimumShakeTime)
     {
         Logger("Shake detected");
         OnShakeDetected(gv);
     }
 }
Пример #8
0
 public override void addValues(GVector gv)
 {
     //calc the acceleration or the longest vector
     double accel = gv.Length; //Math.Sqrt(x * x + y * y + z * z);
     this.Logger(gv.ToString());
     if (accel > _shakeTreshold)
     {
         this.Logger("Shake detected");
         this.OnShakeDetected(gv); //fire the event in the base class
     }
 }
Пример #9
0
	    private static bool CheckForShake(GVector last, GVector current,
	                                        double threshold)
	    {
	        double deltaX = Math.Abs((last.X - current.X));
	        double deltaY = Math.Abs((last.Y - current.Y));
	        double deltaZ = Math.Abs((last.Z - current.Z));
	 
	        return (deltaX > threshold && deltaY > threshold) ||
	                (deltaX > threshold && deltaZ > threshold) ||
	                (deltaY > threshold && deltaZ > threshold);
        }
Пример #10
0
        private static bool CheckForShake(GVector last, GVector current,
                                          double threshold)
        {
            double deltaX = Math.Abs((last.X - current.X));
            double deltaY = Math.Abs((last.Y - current.Y));
            double deltaZ = Math.Abs((last.Z - current.Z));

            return((deltaX > threshold && deltaY > threshold) ||
                   (deltaX > threshold && deltaZ > threshold) ||
                   (deltaY > threshold && deltaZ > threshold));
        }
Пример #11
0
        public override void addValues(GVector gv)
        {
            //calc the acceleration or the longest vector
            double accel = gv.Length; //Math.Sqrt(x * x + y * y + z * z);

            this.Logger(gv.ToString());
            if (accel > _shakeTreshold)
            {
                this.Logger("Shake detected");
                this.OnShakeDetected(gv); //fire the event in the base class
            }
        }
Пример #12
0
        private void CheckForShakes(GVector gv)
        {
            int startIndex = (_shakeRecordIndex - 1);

            if (startIndex < 0)
            {
                startIndex = _minimumShakes - 1;
            }
            int endIndex = _shakeRecordIndex;

            if ((_shakeRecordList[endIndex].EventTime.Subtract(_shakeRecordList[startIndex].EventTime)) <= MinimumShakeTime)
            {
                Logger("Shake detected");
                OnShakeDetected(gv);
            }
        }
Пример #13
0
        public override void addValues(GVector gv)
        {
            now = DateTime.Now;
            //was written for android which uses a normal value of 9.81m/s²
            //cn50 comes with 1/10 G's
            gv = gv.Scale(10);
            double x = gv.X;
            double y = gv.Y;
            double z = gv.Z;
            double force;

            if (lastUpdate.Equals(0))
            {
                this.Logger("ignored due to no update");
                lastUpdate = now;
                lastShake = now;
                lastX = x;
                lastY = y;
                lastZ = z;
            }
            else
            {
                this.Logger("testing for shake");
                timeDiff = now - lastUpdate;
                this.Logger("timediff: "+timeDiff.ToString());
                if (!timeDiff.Equals(0))
                {
                    force = Math.Abs(x + y + z - lastX - lastY - lastZ)
                                / timeDiff.TotalSeconds;
                    
                    this.Logger("force: "+force.ToString() + "treshold: " +_shakeTreshold.ToString());
                    if (force > _shakeTreshold)
                    {
                        if (now - lastShake >= interval)
                        {
                            // trigger shake event
                            this.OnShakeDetected(gv);
                        }
                        lastShake = now;
                    }
                    lastX = x;
                    lastY = y;
                    lastZ = z;
                    lastUpdate = now;
                }
            }
        }
Пример #14
0
        public override void addValues(GVector gv)
        {
            now = DateTime.Now;
            //was written for android which uses a normal value of 9.81m/s²
            //cn50 comes with 1/10 G's
            gv = gv.Scale(10);
            double x = gv.X;
            double y = gv.Y;
            double z = gv.Z;
            double force;

            if (lastUpdate.Equals(0))
            {
                this.Logger("ignored due to no update");
                lastUpdate = now;
                lastShake  = now;
                lastX      = x;
                lastY      = y;
                lastZ      = z;
            }
            else
            {
                this.Logger("testing for shake");
                timeDiff = now - lastUpdate;
                this.Logger("timediff: " + timeDiff.ToString());
                if (!timeDiff.Equals(0))
                {
                    force = Math.Abs(x + y + z - lastX - lastY - lastZ)
                            / timeDiff.TotalSeconds;

                    this.Logger("force: " + force.ToString() + "treshold: " + _shakeTreshold.ToString());
                    if (force > _shakeTreshold)
                    {
                        if (now - lastShake >= interval)
                        {
                            // trigger shake event
                            this.OnShakeDetected(gv);
                        }
                        lastShake = now;
                    }
                    lastX      = x;
                    lastY      = y;
                    lastZ      = z;
                    lastUpdate = now;
                }
            }
        }
Пример #15
0
        /// <summary>
        /// this function uses every direction for its own shake detection
        /// </summary>
        /// <param name="xNew"></param>
        /// <param name="yNew"></param>
        /// <param name="zNew"></param>
        public override void addValues(GVector gv){
            //code is from android where 9.8m/s² is normal
            //cn50 gives 0.98m/s² so we multiply the CN50 values with 10
            gv = gv.Scale(10);

            addToSensorCache(_X_Cache, gv.X);
            addToSensorCache(_Y_Cache, gv.Y);
            addToSensorCache(_Z_Cache, gv.Z);

            this.Logger(gv.ToString());
            if(isShaking(_X_Cache, gv.X) ||
                isShaking(_Y_Cache, gv.Y) ||
                isShaking(_Z_Cache, gv.Z) )
            {
                this.OnShakeDetected(gv);
            }
        }
Пример #16
0
        /// <summary>
        /// this function uses every direction for its own shake detection
        /// </summary>
        /// <param name="xNew"></param>
        /// <param name="yNew"></param>
        /// <param name="zNew"></param>
        public override void addValues(GVector gv)
        {
            //code is from android where 9.8m/s² is normal
            //cn50 gives 0.98m/s² so we multiply the CN50 values with 10
            gv = gv.Scale(10);

            addToSensorCache(_X_Cache, gv.X);
            addToSensorCache(_Y_Cache, gv.Y);
            addToSensorCache(_Z_Cache, gv.Z);

            this.Logger(gv.ToString());
            if (isShaking(_X_Cache, gv.X) ||
                isShaking(_Y_Cache, gv.Y) ||
                isShaking(_Z_Cache, gv.Z))
            {
                this.OnShakeDetected(gv);
            }
        }
Пример #17
0
    private void myThreadStart()
    {
        System.Diagnostics.Debug.WriteLine("Entering thread proc");
        int _i = 0;

        Intermec.Device.Sensor theSensor = new Intermec.Device.Sensor(Intermec.Device.Sensor.SensorsEnabled.AccelerometerEnabled);
        theSensor.SensorDataUpdateInterval = 50;
        ShakeDetection.GVector gv = new ShakeDetection.GVector();
        try
        {
            do
            {
                //The blocking function...
                gv.X = theSensor.Acceleration.GForceX;
                gv.Y = theSensor.Acceleration.GForceY;
                gv.Z = theSensor.Acceleration.GForceZ;

                System.Diagnostics.Debug.WriteLine("Calling into UI thread...");
                Microsoft.WindowsCE.Forms.Message msg = Message.Create(bgWnd.Hwnd, msgID, new IntPtr(_i), IntPtr.Zero);
                //to avoid reading and writing to var at same time from different threads
                lock (theLock)
                {
                    _BGeventArgs._gvector = gv;
                    //the PostMessage is catched in WndProc and the arguments are stored
                    //retrived using the _BGeventArgs variable
                    MessageWindow.PostMessage(ref msg);     //hopefully there is no other posted msg
                }
                System.Diagnostics.Debug.WriteLine("Thread sleeps...");
                _i++;
                Thread.Sleep(50);
            } while (bRunThread);
        }
        catch (ThreadAbortException)
        {
            System.Diagnostics.Debug.WriteLine("Thread will abort");
            bRunThread = false;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine("Exception in ThreadStart: " + ex.Message);
        }
        System.Diagnostics.Debug.WriteLine("ThreadProc ended");
    }
Пример #18
0
        public override void addValues(GVector gv)
        {
            GVector reading = gv;

            myGVqueue.Enqueue(gv);
            if (!firstcall)// _lastReading != null)
            {
                if (myDeltaG.Count == _queueSize)
                {
                    myDeltaG.Dequeue();
                }
                if (myGVqueue.Count == _queueSize)
                {
                    myGVqueue.Dequeue();
                }

                //get and store delta
                GVector gvOld  = myGVqueue.Peek();
                double  deltaG = Math.Abs(gv.Length - gvOld.Length);
                myDeltaG.Enqueue(deltaG);
                //get average
                double sum = 0;
                foreach (double d in myDeltaG)
                {
                    sum += d;
                }
                double DeltaAverage = sum / myDeltaG.Count;
                //rapid movement detection
                if (DeltaAverage > myGmin)
                {
                    if (myDeltaG.Count > myTmin)
                    {
                        OnShakeDetected(gv);
                    }
                }
            }
            else
            {
                myDeltaG.Enqueue(0.9d); //enqueu first value
                firstcall = false;
            }
        }
Пример #19
0
        public override void addValues(GVector gvNew)
        {
            if (FirstTimeEntryFlag != 0)
            {
                //Set new values to old
                gvOld = gvNew;

                //calculate Euclidean distance between old and data points
                double EuclideanDistance = gvNew.EuclideanDistance(gvOld);// Math.Sqrt(Math.Pow(X - Xold, 2) + Math.Pow(Y - Yold, 2) + Math.Pow(Z - Zold, 2));

                //set shake to true if distance between data points is greater than the defined threshold
                if (EuclideanDistance > _shakeTreshold)
                {
                    //System.Diagnostics.Debug.WriteLine("DetectShake : shake detected w/ speed: " + EuclideanDistance.ToString());
                    DetectWindowCount++;
                    if (DetectWindowCount > detectwindow)
                    {
                        DetectWindowCount = 0;
                        ResetWindowCount  = 0;
                        Logger("Shake detected");
                        OnShakeDetected(gvNew);
                    }
                }
                else
                {
                    //movement to low
                    //System.Diagnostics.Debug.WriteLine("DetectShake : speed to low: " + EuclideanDistance.ToString());
                    ResetWindowCount++;
                    if (ResetWindowCount > resetwindow)
                    {
                        DetectWindowCount = 0;
                        ResetWindowCount  = 0;
                        Logger("Windows counts reset");
                        return; //no shake
                    }
                }
            }
            //no longer the first run.
            FirstTimeEntryFlag = 1;
        }
Пример #20
0
        public override void addValues(GVector gv1)
        {
            //calc the acceleration or the longest vector
            double accel = gv1.Length;// Math.Sqrt(x * x + y * y + z * z);

            //we are not interested in directions
            accel = Math.Abs(accel);

            //a low cut filter
            double delta = Math.Abs(accel - accelLast);

            accel = accel * 0.9f + delta;

            this.Logger(gv1.ToString());

            if (accel < _shakeTreshold && accelLast > _shakeTreshold)
            {
                this.OnShakeDetected(gv1);
                this.Logger("Shake detected: " + gv1.ToString());
            }
            accelLast = accel;
        }
Пример #21
0
        public override void addValues(GVector gvNew)
        {
            if (FirstTimeEntryFlag != 0)
            {
                //Set new values to old
                gvOld = gvNew;

                //calculate Euclidean distance between old and data points
                double EuclideanDistance = gvNew.EuclideanDistance(gvOld);// Math.Sqrt(Math.Pow(X - Xold, 2) + Math.Pow(Y - Yold, 2) + Math.Pow(Z - Zold, 2));

                //set shake to true if distance between data points is greater than the defined threshold 
                if (EuclideanDistance > _shakeTreshold)
                {
                    //System.Diagnostics.Debug.WriteLine("DetectShake : shake detected w/ speed: " + EuclideanDistance.ToString());
                    DetectWindowCount++;
                    if (DetectWindowCount > detectwindow) { 
                        DetectWindowCount = 0; 
                        ResetWindowCount = 0;
                        Logger("Shake detected");
                        OnShakeDetected(gvNew); 
                    }
                }
                else
                {
                    //movement to low
                    //System.Diagnostics.Debug.WriteLine("DetectShake : speed to low: " + EuclideanDistance.ToString());
                    ResetWindowCount++;
                    if (ResetWindowCount > resetwindow) {
                        DetectWindowCount = 0; 
                        ResetWindowCount = 0;
                        Logger("Windows counts reset");
                        return; //no shake 
                    }
                }

            }
            //no longer the first run.
            FirstTimeEntryFlag = 1;
        }
Пример #22
0
        public override void addValues(GVector gv1)
        {
            
            //calc the acceleration or the longest vector
            double accel = gv1.Length;// Math.Sqrt(x * x + y * y + z * z);

            //we are not interested in directions
            accel = Math.Abs(accel);

            //a low cut filter
            double delta = Math.Abs(accel - accelLast);
            accel = accel * 0.9f + delta; 
            
            this.Logger(gv1.ToString());
            
            if (accel < _shakeTreshold &&  accelLast>_shakeTreshold)
            {
                this.OnShakeDetected(gv1);
                this.Logger("Shake detected: " + gv1.ToString());
            }
            accelLast = accel;

        }
Пример #23
0
		public override void addValues (GVector gv)
		{
			GVector reading=gv;
            myGVqueue.Enqueue(gv);
            if (!firstcall)// _lastReading != null)
            {
                if (myDeltaG.Count == _queueSize)
                {
                    myDeltaG.Dequeue();
                }
                if (myGVqueue.Count == _queueSize)
                {
                    myGVqueue.Dequeue();
                }

                //get and store delta
                GVector gvOld = myGVqueue.Peek();
                double deltaG = Math.Abs(gv.Length - gvOld.Length);
                myDeltaG.Enqueue(deltaG);
                //get average
                double sum = 0;
                foreach (double d in myDeltaG)
                {
                    sum += d;
                }
                double DeltaAverage = sum / myDeltaG.Count;
                //rapid movement detection
                if (DeltaAverage > myGmin)
                    if (myDeltaG.Count > myTmin)
                        OnShakeDetected(gv);
            }
            else
            {
                myDeltaG.Enqueue(0.9d); //enqueu first value
                firstcall = false;
            }
		}
Пример #24
0
        public override void addValues(GVector gv)
		{
            //use new GVector 16 segmented direction
            GVector.Direction direction = gv.direction;

            double accel = gv.Length;// Math.Sqrt(e.X * e.X + e.Y * e.Y);// + e.Z*e.Z);
            //Does the currenet acceleration vector meet the minimum magnitude that we
            //care about?
            if (accel > MinimumAccelerationMagnitudeSquared)
            {
                //If the shake detected is in the same direction as the last one then ignore it
                //if ((direction & _shakeRecordList[_shakeRecordIndex].ShakeDirection) != GVector.Direction.None)// Direction.None)
                if((direction == _shakeRecordList[_shakeRecordIndex].ShakeDirection))
                    return;
                ShakeRecord record = new ShakeRecord();
                record.EventTime = DateTime.Now;
                record.ShakeDirection = direction;
                _shakeRecordIndex = (_shakeRecordIndex + 1)%_minimumShakes;
                _shakeRecordList[_shakeRecordIndex] = record;

                 CheckForShakes(gv);

            }
        }
Пример #25
0
        public override void addValues(GVector gv)
        {
            //private static int SHAKE_THRESHOLD = 800;

            long lastUpdate = 0;
            long curTime    = System.DateTime.Now.Millisecond;

            // only allow one update every 100ms.
            if ((curTime - lastUpdate) > 100)
            {
                long diffTime = (curTime - lastUpdate);
                lastUpdate = curTime;

                double speed = Math.Abs(gv.X + gv.Y + gv.Z - last_x - last_y - last_z) / diffTime * 10000;

                //adjust for CN50
                speed = speed / 10;
                if (speed > _shakeTreshold)
                {
                    this.Logger("Shake detected");
                    this.OnShakeDetected(gv);
                }
                else
                {
                    //System.Diagnostics.Debug.WriteLine("sensor : shake speed: " + speed.ToString());
                }
                gvLast = gv;
                last_x = gv.X;
                last_y = gv.Y;
                last_z = gv.Z;
            }
            else
            {
                this.Logger("sensor : timespan to low ");
            }
        }
Пример #26
0
 /// <summary>
 /// implement this with the actual vector and use OnShakeEvent to signal a 'Shake'
 /// </summary>
 /// <param name="gv"></param>
 public abstract void addValues(GVector gv);
Пример #27
0
 /// <summary>
 /// this eventhandler should be fired by the derived classes to signal a shake detection
 /// </summary>
 /// <param name="gv"></param>
 public void OnShakeDetected(GVector gv)
 {
     ShakeDetectedEvent(this, new ShakeEventArgs(gv));
 }
Пример #28
0
        /// <summary>
        /// implement this with the actual vector and use OnShakeEvent to signal a 'Shake'
        /// </summary>
        /// <param name="gv"></param>
		public abstract void addValues(GVector gv);
Пример #29
0
 public ShakeEventArgs(GVector gvector)
     : base()
 {
     this._gvector = gvector;
 }
Пример #30
0
 /// <summary>
 /// calc the distance between the end of two vectors
 /// </summary>
 /// <param name="gvOld">the vector to compare to</param>
 /// <returns></returns>
 public double EuclideanDistance(GVector gvOld)
 {
     return(Math.Sqrt(Math.Pow(this.X - gvOld.X, 2) + Math.Pow(this.Y - gvOld.Y, 2) + Math.Pow(this.Z - gvOld.Z, 2)));
 }
Пример #31
0
 /// <summary>
 /// this eventhandler should be fired by the derived classes to signal a shake detection
 /// </summary>
 /// <param name="gv"></param>
 public void OnShakeDetected(GVector gv)
 {
     ShakeDetectedEvent(this, new ShakeEventArgs(gv));
 }
Пример #32
0
 public long TicksDiff(GVector gvNew)
 {
     GVector gvOld=this;
     long diff = gvNew.Ticks - gvOld.Ticks;
     //one tick is 100 nano seconds
     return diff;
 }
Пример #33
0
 /// <summary>
 /// calc the distance between the end of two vectors
 /// </summary>
 /// <param name="gvOld">the vector to compare to</param>
 /// <returns></returns>
 public double EuclideanDistance(GVector gvOld)
 {
     return Math.Sqrt(Math.Pow(this.X - gvOld.X, 2) + Math.Pow(this.Y - gvOld.Y, 2) + Math.Pow(this.Z - gvOld.Z, 2));
 }
Пример #34
0
        private void myThreadStart()
        {
            System.Diagnostics.Debug.WriteLine("Entering thread proc");
            int _i=0;
            Intermec.Device.Sensor theSensor = new Intermec.Device.Sensor(Intermec.Device.Sensor.SensorsEnabled.AccelerometerEnabled);
            theSensor.SensorDataUpdateInterval = 50;
            ShakeDetection.GVector gv = new ShakeDetection.GVector();
            try
            {
                do
                {
                    //The blocking function...
                    gv.X = theSensor.Acceleration.GForceX;
                    gv.Y = theSensor.Acceleration.GForceY;
                    gv.Z = theSensor.Acceleration.GForceZ;

                    System.Diagnostics.Debug.WriteLine("Calling into UI thread...");
                    Microsoft.WindowsCE.Forms.Message msg = Message.Create(bgWnd.Hwnd, msgID, new IntPtr(_i), IntPtr.Zero);
                    //to avoid reading and writing to var at same time from different threads
                    lock (theLock)
                    {
                        _BGeventArgs._gvector = gv;
                        //the PostMessage is catched in WndProc and the arguments are stored
                        //retrived using the _BGeventArgs variable
                        MessageWindow.PostMessage(ref msg); //hopefully there is no other posted msg
                    }
                    System.Diagnostics.Debug.WriteLine("Thread sleeps...");
                    _i++;
                    Thread.Sleep(50);
                } while (bRunThread);
            }
            catch (ThreadAbortException)
            {
                System.Diagnostics.Debug.WriteLine("Thread will abort");
                bRunThread = false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Exception in ThreadStart: " + ex.Message);
            }
            System.Diagnostics.Debug.WriteLine("ThreadProc ended");
        }
Пример #35
0
 public ShakeEventArgs(GVector gvector)
     : base()
 {
     this._gvector = gvector;
 }