Android Open Source - cwp-morse-mangle C W Input






From Project

Back to project page cwp-morse-mangle.

License

The source code is released under:

Copyright (C) 2012 Jussi Kivilinna <jussi.kivilinna@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "S...

If you think the Android project cwp-morse-mangle listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2012 Jussi Kivilinna <jussi.kivilinna@gmail.com>
 */*from ww  w . ja  va 2s .c om*/
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package fi_81.cwp_morse_mangle.cwp;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayDeque;

import fi_81.cwp_morse_mangle.cwp.CWInputQueue;
import fi_81.cwp_morse_mangle.morse.BitString;
import fi_81.cwp_morse_mangle.morse.MorseCodec;

public class CWInput {
  /* Callbacks from input, frequency change, state changes and morse messages */
  public interface CWInputNotification {
    public abstract void frequencyChange(long frequency);

    public abstract void stateChange(byte newState, int value);

    public abstract void morseMessage(BitString morseBits);
  }

  public static class NotificationNone implements CWInputNotification {
    public void frequencyChange(long newFreq) {
    }

    public void stateChange(byte newState, int value) {
    }

    public void morseMessage(BitString morseBits) {
    }
  }

  private final int MAX_BUFFER_PAST = 100000; /* 100 sec */

  private long currFreq = 1;
  private ByteBuffer inBuf;
  private CWInputQueue morseQueue;
  private ArrayDeque<CWStateChange> bufferQueue;
  private final CWaveQueueToMorseCode morseDecoder = new CWaveQueueToMorseCode();
  private long lastReceivedWaveTime;

  private int lastStateUpValue;
  private long lastReceivedStateTime;
  private int maxBufferLength;
  private int bufferLength;
  private long connStartTime;

  public CWInput(CWInputQueue queue, ByteBuffer bb) {
    maxBufferLength = 0;
    bufferLength = 0;
    lastStateUpValue = 0;
    lastReceivedStateTime = 0;

    lastReceivedWaveTime = 0;
    morseQueue = queue;
    bufferQueue = new ArrayDeque<CWStateChange>();

    if (bb == null) {
      /* Allocate IO buffer and set it to big-endian byteorder */
      inBuf = ByteBuffer.allocateDirect(128);
      inBuf.order(ByteOrder.BIG_ENDIAN);
    } else {
      inBuf = bb;
      inBuf.compact();
    }
  }

  public CWInput(ByteBuffer bb) {
    this(new CWInputQueue(), bb);
  }

  public CWInput() {
    this(new CWInputQueue(), null);
  }

  public CWInput(int maxBufferLen, long connectionStartTime) {
    this(new CWInputQueue(), null);

    connStartTime = connectionStartTime;
    maxBufferLength = maxBufferLen;
  }

  public ByteBuffer getInBuffer() {
    return inBuf;
  }

  public void processInput(final CWInputNotification notify) {
    inBuf.flip();

    while (inBuf.remaining() > 0) {
      boolean out = false;

      switch (morseQueue.getCurrentState()) {
      case CWave.TYPE_DOWN:
        /* In down state, expect 4 bytes integer input */
        if (inBuf.remaining() < 4) {
          out = true;
          break;
        }

        processInputDown(notify);
        break;
      case CWave.TYPE_UP:
        /* In up state, expect 2 bytes integer input */
        if (inBuf.remaining() < 2) {
          out = true;
          break;
        }

        processInputUp(notify);
        break;
      }

      if (out)
        break;

      /*
       * Process new buffered state changes before morse decoding, as
       * morse decoding induces more latency and jitter.
       */
      processBufferedStateChanges(notify);

      /*
       * After receiving data, decode buffered wave-form to morse code.
       * Decode after each received message, as this enforces same
       * behavior in test-cases as in real world.
       */
      BitString morseBits;
      do {
        morseBits = morseDecoder.tryDecode(morseQueue, false);
        if (morseBits != null)
          notify.morseMessage(morseBits);
      } while (morseBits != null);
    }

    inBuf.compact();

    /*
     * Process buffered state changes (this is here in case there was no new
     * input, to processing delayed state changes)
     */
    processBufferedStateChanges(notify);

    /*
     * Let morseDecoder to flush too old stale morse bits
     */
    boolean forceFlush = false;

    if (morseQueue.getQueue().size() > MorseCodec.endSequence.length())
      forceFlush = true;
    else if (timeToNextWork() == 0)
      forceFlush = true;

    flushStaleMorseBits(notify, forceFlush);
    if (forceFlush)
      lastReceivedWaveTime = 0;
  }

  private void processInputDown(final CWInputNotification notify) {
    int value = inBuf.getInt();

    /* At down state, so this is either state-change:up or freq-change */

    /* Negative value means frequency-change. */
    if (value < 0) {
      long newFreq = -(long) value;
      /*
       * Frequency value is negated, so de-negate before passing
       */
      notify.frequencyChange(newFreq);

      if (newFreq != currFreq) {
        /* Force flush morse buffer since channel changed */
        flushStaleMorseBits(notify, true);

        currFreq = newFreq;
      }
    } else {
      long currTime = System.currentTimeMillis();

      morseQueue.pushStateUp(value);

      /* Latency management for visualizing received state changes */
      if (maxBufferLength > 0) {
        adjustBufferLength(value, currTime);

        bufferQueue.add(new CWStateChange(
            CWStateChange.TYPE_DOWN_TO_UP, value, bufferLength
                + value));

        lastStateUpValue = value;
      } else {
        /* No buffering if max buffer length set to zero */
        notify.stateChange(CWave.TYPE_UP, value);
      }

      lastReceivedStateTime = currTime;
    }
  }

  private void processInputUp(final CWInputNotification notify) {
    long currTime = System.currentTimeMillis();
    int value = inBuf.getShort();

    /*
     * 16bit unsigned integer value get sign-extended, so mask out upper
     * bits
     */
    value = value & 0xffff;

    /* At up state, so this must be state-change:down */
    morseQueue.pushStateDown(value);

    /* Latency management for visualizing received state changes */
    if (maxBufferLength > 0) {
      adjustBufferLength(lastStateUpValue + value, currTime);

      bufferQueue.add(new CWStateChange(CWStateChange.TYPE_UP_TO_DOWN,
          value, bufferLength + lastStateUpValue + value));
    } else {
      /* No buffering if max buffer length set to zero */
      notify.stateChange(CWave.TYPE_DOWN, value);
    }

    lastReceivedWaveTime = currTime;
    lastReceivedStateTime = currTime;
  }

  private void processBufferedStateChanges(final CWInputNotification notify) {
    /*
     * Process buffered data in-order, so that state changes in past will be
     * processed as 0ms length waves.
     */
    while (timeToNextQueueWork() == 0) {
      CWStateChange state = bufferQueue.remove();

      if (state.type == CWStateChange.TYPE_DOWN_TO_UP)
        notify.stateChange(CWave.TYPE_UP, state.value);
      else
        notify.stateChange(CWave.TYPE_DOWN, state.value);
    }
  }

  private void adjustBufferLength(int timeStamp, long currTime) {
    /* Adjust buffer length depending on current network latency */
    long currConnTime = currTime - connStartTime;
    int latency = (int) (currConnTime - timeStamp);
    int timeSinceLastState = (int) (currTime - lastReceivedStateTime);

    /*
     * Adjust buffer length as specified in CWP Spec v1.1,
     * "6 Latency Management"
     */
    if (latency <= bufferLength) {
      bufferLength -= (timeSinceLastState * (bufferLength + latency))
          / MAX_BUFFER_PAST;
    } else {
      bufferLength = latency;
    }

    if (bufferLength < 0)
      bufferLength = 0;
    else if (bufferLength > maxBufferLength)
      bufferLength = maxBufferLength;
  }

  public void flushStaleMorseBits(CWInputNotification notify, boolean force) {
    BitString morseBits;

    /* Force decoding */
    if (force) {
      do {
        morseBits = morseDecoder.tryDecode(morseQueue, true);
        if (morseBits != null) {
          notify.morseMessage(morseBits);
          continue;
        }
      } while (morseBits != null);
    }

    /* Flush morse buffer, either by force or by timeout */
    morseBits = morseDecoder.flushStalled(force);
    if (morseBits != null)
      notify.morseMessage(morseBits);
  }

  public boolean hadPendingBits() {
    return morseDecoder.hadPendingBits();
  }

  /* Time to next latency management delayed work */
  private long timeToNextQueueWork() {
    if (maxBufferLength <= 0 || bufferQueue.isEmpty())
      return Long.MAX_VALUE;

    long currentTime = System.currentTimeMillis();
    long timeSinceConnCreation = currentTime - connStartTime;
    long timeToNext = bufferQueue.peek().getOutTime()
        - timeSinceConnCreation;

    if (timeToNext < 0)
      return 0;

    return timeToNext;
  }

  /* Time to next morse decoder delayed work */
  private long timeToNextMorseWork() {
    /* If no received waves, don't wait */
    if (lastReceivedWaveTime <= 0)
      return Long.MAX_VALUE;

    /* If less than zero, no data to be flushed */
    long flushTimeout = morseDecoder.getFlushTimeout();
    if (flushTimeout < 0)
      return Long.MAX_VALUE;

    /* Get time to next forced flush of morse-decoder */
    long timeToFlush = (lastReceivedWaveTime + flushTimeout)
        - System.currentTimeMillis();

    /* If timeout has passed return zero */
    if (timeToFlush <= 0)
      return 0;

    return timeToFlush;
  }

  public long timeToNextWork() {
    return Math.min(timeToNextQueueWork(), timeToNextMorseWork());
  }
}




Java Source Code List

fi_81.cwp_morse_mangle.CWPControlService.java
fi_81.cwp_morse_mangle.CWPControlThread.java
fi_81.cwp_morse_mangle.DefaultSettings.java
fi_81.cwp_morse_mangle.EventLog.java
fi_81.cwp_morse_mangle.MainActivity.java
fi_81.cwp_morse_mangle.MainSettingsActivity.java
fi_81.cwp_morse_mangle.SinAudioLoop.java
fi_81.cwp_morse_mangle.cwp.CWFrequencyChange.java
fi_81.cwp_morse_mangle.cwp.CWInputQueue.java
fi_81.cwp_morse_mangle.cwp.CWInput.java
fi_81.cwp_morse_mangle.cwp.CWOutput.java
fi_81.cwp_morse_mangle.cwp.CWStateChangeQueueFromMorseCode.java
fi_81.cwp_morse_mangle.cwp.CWStateChange.java
fi_81.cwp_morse_mangle.cwp.CWaveQueueToMorseCode.java
fi_81.cwp_morse_mangle.cwp.CWave.java
fi_81.cwp_morse_mangle.morse.BitString.java
fi_81.cwp_morse_mangle.morse.MorseCharList.java
fi_81.cwp_morse_mangle.morse.MorseChar.java
fi_81.cwp_morse_mangle.morse.MorseCodec.java