Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import

  • Loading branch information...
commit a2b93cf59c8fa22a7bcd2c3aaa71905da40159be 0 parents
@sivantoledo authored
Showing with 3,929 additions and 0 deletions.
  1. BIN  manual.doc
  2. +162 −0 src/SoundcardInterface.java
  3. +78 −0 src/SoundcardWriteThread.java
  4. +138 −0 src/TNCConnectThread.java
  5. +376 −0 src/TNCInterface.java
  6. +33 −0 src/TNCProcMonitor.java
  7. +58 −0 src/TNCQueue.java
  8. +36 −0 src/TNCSerialInterface.java
  9. +26 −0 src/TNCWriteThread.java
  10. +464 −0 src/sivantoledo/ax25/Afsk1200Demodulator.java
  11. +71 −0 src/sivantoledo/ax25/Afsk1200Filters.java
  12. +223 −0 src/sivantoledo/ax25/Afsk1200Modulator.java
  13. +109 −0 src/sivantoledo/ax25/Afsk1200MultiDemodulator.java
  14. +165 −0 src/sivantoledo/ax25/Filter.java
  15. +399 −0 src/sivantoledo/ax25/Packet.java
  16. +25 −0 src/sivantoledo/ax25/PacketDemodulator.java
  17. +24 −0 src/sivantoledo/ax25/PacketHandler.java
  18. +27 −0 src/sivantoledo/ax25/PacketModulator.java
  19. +85 −0 src/sivantoledo/ax25/SerialTransmitController.java
  20. +249 −0 src/sivantoledo/ax25/Soundcard.java
  21. +30 −0 src/sivantoledo/ax25/SoundcardConsumer.java
  22. +31 −0 src/sivantoledo/ax25/SoundcardProducer.java
  23. +380 −0 src/sivantoledo/ax25/Test.java
  24. +27 −0 src/sivantoledo/ax25/TransmitController.java
  25. +642 −0 src/sivantoledo/ax25/XXXAfsk1200.java
  26. +32 −0 src/sivantoledo/ax25/XXXHalfduplexSoundcardClient.java
  27. +39 −0 test.bat
BIN  manual.doc
Binary file not shown
162 src/SoundcardInterface.java
@@ -0,0 +1,162 @@
+import java.util.*;
+import java.io.*;
+import java.net.*;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.Mixer;
+import javax.sound.sampled.SourceDataLine;
+import javax.sound.sampled.TargetDataLine;
+
+import com.ae5pl.nsutil.*;
+
+import javax.sound.sampled.*;
+import sivantoledo.ax25.*;
+
+/**
+ * An interface to Sivan Toledo's sound-card modem for javAPRSIGate.
+ * <BR>
+ * Some code used by permission from Roger Bille SM5NRK
+ */
+final public class SoundcardInterface extends TNCConnectThread
+ implements sivantoledo.ax25.PacketHandler
+{
+
+ private String soundin, soundout;
+ private int rate;
+ private int latency_ms;
+ //sivantoledo.ax25.Afsk1200 afsk;
+ sivantoledo.ax25.Afsk1200Modulator modulator;
+ sivantoledo.ax25.PacketDemodulator demodulator;
+ sivantoledo.ax25.Soundcard sc;
+
+ private int TXDelay = 20; // KISS specification default is 50 (= 500ms)
+ private int Persist = 255; // KISS specification default is 63 (p = 0.25)
+ private int SlotTime = 0; // KISS specification default is 10 (= 100ms)
+ private int FullDuplex = 0; // KISS specification default is 0 (= half duplex)
+ private int TXTail = 10; // KISS specification says this is obsolete
+
+ //private final nsByteArrayOutputStream inbuf = new nsByteArrayOutputStream(1024);
+
+ private TransmitController ptt;
+
+ SoundcardInterface()
+ {
+ version = "Soundcard Interface; Copyright © 2005 - Pete Loveall AE5PL;";
+ }
+
+ /**
+ * Closes the serial port connection.
+ */
+ public final synchronized void close()
+ {
+ }
+
+ /**
+ * Returns version info for inclusion on status page.
+ */
+ public String getVersion()
+ {
+ return version+"; Sivan Toledo 2012";
+ }
+
+ public synchronized void init(Properties configuration)
+ {
+ initParams(configuration);
+ if (firstTime)
+ {
+ firstTime = false;
+
+ //try {TNCPort = Integer.parseInt(configuration.getProperty("SoundcardTNCDevice", "1").trim());}
+ //catch (Exception e){System.err.println("Exception parsing KISSTNCPortNumber "+e.toString());}
+ //tncPort =(byte)((TNCPort-1)<<4);
+ try {TXDelay = (Integer.parseInt(configuration.getProperty("KISSTXDelay", "200").trim())/10);}
+ catch (Exception e){System.err.println("Exception parsing KISSTXDelay "+e.toString());}
+ try {Persist = Integer.parseInt(configuration.getProperty("KISSPersist", "255").trim());}
+ catch (Exception e){System.err.println("Exception parsing KISSPersist "+e.toString());}
+ try {SlotTime = (Integer.parseInt(configuration.getProperty("KISSSlotTime", "0").trim()));}
+ catch (Exception e){System.err.println("Exception parsing KISSPersist "+e.toString());}
+ boolean fulldux = false;
+ try {fulldux = Boolean.valueOf(configuration.getProperty("KISSFullDuplex", "false").trim()).booleanValue();}
+ catch (Exception e){System.err.println("Exception parsing KISSFullDuplex "+e.toString());}
+ if (fulldux) FullDuplex = 1;
+ try {TXTail = (Integer.parseInt(configuration.getProperty("KISSTXTail", "100").trim())/10);}
+ catch (Exception e){System.err.println("Exception parsing KISSTXTail "+e.toString());}
+
+ try {rate = Integer.parseInt(configuration.getProperty("SoundcardSampleRate", "9600").trim());}
+ catch (Exception e){System.err.println("Exception parsing SoundcardSampleRate "+e.toString());}
+
+ try {latency_ms = Integer.parseInt(configuration.getProperty("SoundcardLatency", "100").trim());}
+ catch (Exception e){System.err.println("Exception parsing SoundcardLatency "+e.toString());}
+
+ //if (!configuration.containsKey("SoundCardName"))
+ // configuration.put("SerialPortName", configuration.getProperty("KISSPortName", ""));
+ String soundcard = configuration.getProperty("SoundcardName", "default").trim();
+ soundin = configuration.getProperty("SoundcardInputName", "default").trim();
+ soundout = configuration.getProperty("SoundcardOutputName", "default").trim();
+ if (soundin.equals("default")) soundin = soundcard;
+ if (soundout.equals("default")) soundout = soundcard;
+
+ System.err.println("Starting up Afsk1200 modem on ["+soundin+", "+soundout+"] at "+rate+" samples/s");
+
+ try {
+ modulator = new sivantoledo.ax25.Afsk1200Modulator(rate);
+ demodulator = new sivantoledo.ax25.Afsk1200MultiDemodulator(rate,this);
+ //afsk = new sivantoledo.ax25.Afsk1200(rate, this);
+ modulator.setTxDelay(TXDelay);
+ } catch (Exception e) {
+ System.err.println("Afsk1200 constructor exception: "+e.getMessage());
+ System.exit(1);
+ }
+
+ String ptt_port = configuration.getProperty("PTTPort", "none").trim();
+ String ptt_signal = configuration.getProperty("PTTSignal", "RTS").trim();
+ if (!ptt_port.equals("none")) {
+ try {
+ ptt = new SerialTransmitController(ptt_port,ptt_signal);
+ System.err.println("Opened a serial PTT port: "+ptt_port);
+ } catch (Exception e) {
+ System.err.println("PTT initialization error: "+e.getMessage());
+ ptt=null;
+ }
+ } else {
+ System.err.println("Warning: No PTT port (okay for receive only or for VOX)");
+ }
+
+
+ sc = new sivantoledo.ax25.Soundcard(rate, soundin, soundout, latency_ms,
+ demodulator, modulator);
+ new Thread(this, "SoundInterface Read").start();
+ }
+ }
+
+ public void handlePacket(byte[] packet) {
+ try {
+ TNCInterface.AX25Packet tnc = new TNCInterface.AX25Packet((byte)0, packet);
+ if (tnc.restOfPacket.length > 0)
+ queue.putOnQueue(tnc);
+ }
+ catch (Exception e){}
+ }
+
+ public void run() {
+ //if (serial == null) return;
+
+ // Init TNC
+ try
+ {
+ writer = new SoundcardWriteThread(demodulator, modulator,
+ sc,
+ (double) Persist / 255.0,SlotTime,
+ ptt);
+ }
+ catch (Exception e)
+ {
+ System.err.println("Error initializing Soundcard "+e.toString());
+ return;
+ }
+
+ sc.receive();
+ }
+}
78 src/SoundcardWriteThread.java
@@ -0,0 +1,78 @@
+import java.io.*;
+import java.util.*;
+
+import sivantoledo.ax25.TransmitController;
+
+import com.ae5pl.nsutil.*;
+
+final class SoundcardWriteThread extends TNCWriteThread {
+ //private sivantoledo.ax25.Afsk1200 afsk;
+ private sivantoledo.ax25.Soundcard sc;
+
+ private sivantoledo.ax25.PacketDemodulator demodulator;
+ private sivantoledo.ax25.PacketModulator modulator;
+
+ private double persistence; // CSMA access probability
+ private int slot_time; // wait between CSMA attemps in 10ms units
+
+ private TransmitController ptt;
+
+ SoundcardWriteThread(//sivantoledo.ax25.Afsk1200 afsk,
+ sivantoledo.ax25.PacketDemodulator demodulator,
+ sivantoledo.ax25.PacketModulator modulator,
+ sivantoledo.ax25.Soundcard sc,
+ double persistence, int slot_time,
+ TransmitController ptt
+ ) {
+ super();
+ this.ptt = ptt;
+ //this.afsk = afsk;
+ this.demodulator = demodulator;
+ this.modulator = modulator;
+ this.sc = sc;
+ this.persistence = persistence;
+ this.slot_time = slot_time;
+ setName("Soundcard Write Thread");
+ start();
+ }
+
+ public void run() {
+ // if (output == null) return;
+ for (;;) {
+ TNCInterface.AX25Packet outline = (TNCInterface.AX25Packet) queue
+ .getFromQueue();
+ if (outline == null)
+ return;
+
+ byte[] newpacket = outline.getAX25Packet();
+ if (newpacket == null) {
+ yield();
+ continue;
+ }
+ try {
+ //output.write(newpacket);
+ // does the packet include the CRC, or do we need to add it?
+ // the contstructor of Packet does add the CRC, so hopefully it it not
+ // included.
+
+ while (demodulator.dcd()) yield(); // wait for a channel that is not busy
+ while (Math.random() > persistence) {
+ try { sleep(10*slot_time); } catch (InterruptedException ie) {}
+ while (demodulator.dcd()) yield(); // wait for a channel that is not busy
+ }
+
+ modulator.prepareToTransmit(new sivantoledo.ax25.Packet(newpacket));
+ // key PTT
+ if (ptt != null) ptt.startTransmitter();
+ sc.transmit();
+ // unkey PTT; we have drained the audio buffer
+ if (ptt != null) ptt.stopTransmitter();
+
+ pace(outline);
+ } catch (Exception e) {
+ queue.putOnQueue(null);
+ }
+ yield();
+ }
+ }
+}
138 src/TNCConnectThread.java
@@ -0,0 +1,138 @@
+import java.util.*;
+
+/**
+ * Override this class to do your connection initialization and read functions.
+ */
+abstract class TNCConnectThread extends TNCInterface implements Runnable
+{
+ final TNCQueue queue = new TNCQueue();
+ volatile TNCWriteThread writer = null;
+
+ protected String version;
+
+ protected boolean firstTime = true;
+
+ /**
+ * Returns version info for inclusion on status page.
+ */
+ public String getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Determines if connected to physical interface.
+ */
+ public boolean isConnected()
+ {
+ if (writer == null)
+ return false;
+ return writer.queue.isEnabled();
+ }
+
+ /**
+ * Get next received packet.
+ * <P>
+ * Must be synchronized to enable wait for next packet.
+ *
+ * @return TNC2 formated packet, no cr or lf
+ */
+ public TNCInterface.AX25Packet receivePacket()
+ {
+ return queue.getFromQueue();
+ }
+
+ /**
+ * This puts a packet on the TNC receive queue.
+ * <P>
+ * Must be synchronized.
+ * The packet is in TNC2 format.
+ *
+ * @param packet Complete packet including TNC2 header.
+ */
+ public void sendInternetPacket(TNCInterface.AX25Packet packet)
+ {
+ queue.putOnQueue(packet);
+ }
+
+ /**
+ * Submit packet for transmission.
+ * <P>
+ * Must be synchronized to allow placing packet on queue.
+ * Do not delay the calling process.
+ * <P>
+ * The packet is in TNC2 format.
+ *
+ * @param packet Complete packet including TNC2 header.
+ */
+ public void sendPacket(TNCInterface.AX25Packet packet)
+ {
+ if (writer != null)
+ {
+ queue.putOnQueue(packet); // For logging
+ writer.queue.putOnQueue(packet);
+ }
+ }
+
+ /**
+ * Override this function with your operating code
+ */
+ abstract public void run();
+
+ protected void initParams(Properties configuration)
+ {
+ try {TNCWriteThread.TNCSpeed = Integer.parseInt(configuration.getProperty("TNCSpeed", "1200").trim());}
+ catch (Exception e)
+ {
+ System.err.println("Exception parsing TNCSpeed");
+ e.printStackTrace();
+ }
+
+ try {TNCWriteThread.TNCIMax = Integer.parseInt(configuration.getProperty("TNCIFieldMax", "256").trim());}
+ catch (Exception e)
+ {
+ System.err.println("Exception parsing TNCIFieldMax");
+ e.printStackTrace();
+ }
+
+ try {TNCWriteThread.TNCVias = Integer.parseInt(configuration.getProperty("TNCMaxVias", "7").trim());}
+ catch (Exception e)
+ {
+ System.err.println("Exception parsing TNCMaxVias");
+ e.printStackTrace();
+ }
+
+ if (firstTime)
+ {
+ String TNCInit = configuration.getProperty("TNCPortInit", "");
+ if (TNCInit.length() > 0)
+ {
+ try
+ {
+ boolean waitforend = true;
+ try {waitforend = Boolean.valueOf(configuration.getProperty("TNCPortInitWait", "true").trim()).booleanValue();}
+ catch (Exception be)
+ {
+ System.err.println("Exception parsing TNCPortInitWait");
+ be.printStackTrace();
+ }
+ Process pr = Runtime.getRuntime().exec(TNCInit);
+ Thread tpo = new TNCProcMonitor(pr.getInputStream(), System.err);
+ Thread tpe = new TNCProcMonitor(pr.getErrorStream(), System.err);
+ if (waitforend)
+ {
+ pr.waitFor();
+ // The following are to ensure proper error log printing
+ tpo.join();
+ tpe.join();
+ }
+ }
+ catch (Exception e)
+ {
+ System.err.println("Error trying to run "+TNCInit);
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
376 src/TNCInterface.java
@@ -0,0 +1,376 @@
+import java.util.*;
+import java.io.*;
+import com.ae5pl.nsutil.*;
+/**
+ * This interface must be implemented by any TNC interface module.
+ * <P>
+ * Synchronization Note:
+ * <P>
+ * It is expected that any thread-safe synchronization be done by the interface module.
+ * For instance, it is possible that all methods may be called from different threads.
+ * <PRE>
+ * Changed from 2.1:
+ * Packet support to use AX25Packet class
+ * Changed from 2.0:
+ * Added byte to receivePacket() for source indicator
+ * Changed from 1.1:
+ * Moved all TNC2 formatting into javAPRSIGate.
+ * Changed from 1.0:
+ * Added portTable comment to getVersion description
+ * </PRE>
+ * Requires javAPRSIGate 2.0 or higher
+ *
+ * @author Peter Loveall AE5PL
+ * @version 3.0
+ */
+public abstract class TNCInterface
+{
+ /**
+ * Allows port to be reset or closed.
+ *
+ * Must be synchronized.
+ * Should be called by TNC.finalize()
+ */
+ public abstract void close();
+
+ /**
+ * Returns version info for status pages.
+ * <P>
+ * The format is the same as for portTable in javAPRSSrvr.
+ *
+ * @return This can be any text string to be displayed on the status pages.
+ */
+ public abstract String getVersion();
+
+ /**
+ * Initializes interface.
+ * <P>
+ * This may be called multiple times so
+ * must be synchronized to not interfere with receivePacket, sendPacket, and close.
+ *
+ * @param configuration Properties from configuration file
+ */
+ public abstract void init(Properties configuration);
+
+ /**
+ * Determines if connected to physical interface.
+ */
+ public abstract boolean isConnected();
+
+ /**
+ * Get next received packet.
+ * <P>
+ * Must be synchronized to enable wait for next packet.
+ * <P>
+ * The AX25Packet.rcvd indicates 0=Received from RF, 1=Send to Inet, 2=Send to RF
+ *
+ * @return Packet
+ */
+ public abstract AX25Packet receivePacket();
+
+ /**
+ * This puts a packet on the TNC receive queue.
+ * <P>
+ * Must be synchronized.
+ *
+ * @param packet Complete packet.
+ */
+ public abstract void sendInternetPacket(AX25Packet packet);
+
+ /**
+ * Submit packet for transmission.
+ * <P>
+ * Must be synchronized to allow placing packet on queue.
+ * Do not delay the calling process.
+ *
+ * @param packet Complete packet.
+ */
+ public abstract void sendPacket(AX25Packet packet);
+
+
+ /**
+ * @return UI packet with PID of 0xf0
+ */
+ public final static AX25Packet getAPRSPacket(byte rcvd, String[] addresses, int digied, byte[] IField)
+ {
+ byte[] restOfPacket = new byte[IField.length+2];
+ restOfPacket[0] = 0x03;
+ restOfPacket[1] = (byte)0xf0;
+ System.arraycopy(IField, 0, restOfPacket, 2, IField.length);
+ byte[] ssids = new byte[addresses.length];
+ for (int i = 0; i < ssids.length; i++)
+ {
+ ssids[i] = 0x60;
+ }
+ return new AX25Packet(rcvd, addresses, ssids, digied, restOfPacket);
+ }
+
+ /**
+ * @param rcvd Sets AX25Packet.rcvd
+ * @param TNC2Format TNC2Format line (no CR/LF)
+ * @return UI packet with PID of 0xf0
+ */
+ public final static AX25Packet getAPRSPacket(byte rcvd, byte[] TNC2Format)
+ {
+ StringTokenizer TNC2Parse = null;
+ int colon = -1;
+ for (int i = 0; i < TNC2Format.length; i++)
+ {
+ if (TNC2Format[i] == (byte)':')
+ {
+ colon = i;
+ TNC2Parse = new StringTokenizer(new String(TNC2Format, 0, 0, i), ">,");
+ break;
+ }
+ }
+ String[] addresses = new String[TNC2Parse.countTokens()];
+ addresses[1] = TNC2Parse.nextToken();
+ addresses[0] = TNC2Parse.nextToken();
+ int digied = 1;
+ for (int i = 2; TNC2Parse.hasMoreTokens(); i++)
+ {
+ addresses[i] = TNC2Parse.nextToken();
+ if (addresses[i].endsWith("*"))
+ {
+ addresses[i] = addresses[i].substring(0, addresses[i].length()-1);
+ digied = i;
+ }
+ }
+ byte[] ssids = new byte[addresses.length];
+ for (int i = 0; i < ssids.length; i++)
+ {
+ ssids[i] = 0x60;
+ }
+
+ byte[] restOfPacket = new byte[TNC2Format.length+1-colon];
+ restOfPacket[0] = 0x03;
+ restOfPacket[1] = (byte)0xf0;
+ if (restOfPacket.length > 2)
+ System.arraycopy(TNC2Format, colon+1, restOfPacket, 2, restOfPacket.length-2);
+ return new AX25Packet(rcvd, addresses, ssids, digied, restOfPacket);
+ }
+
+ static class AX25Packet
+ {
+ /**
+ * This byte indicates 0=Received from RF, 1=Send to Inet, 2=Send to RF
+ */
+ public final byte rcvd;
+
+ /**
+ * This is the address fields in ASCII. Non-AX.25 SSIDs are allowed but
+ * will be invalid if used with an AX.25 TNC.<BR>
+ * Addresses are as follows:
+ * <PRE>
+ * 0 - Destination
+ * 1 - Source
+ * 2 - Digipeater Zero
+ * 3 - Digipeater One
+ * ...
+ * </PRE>
+ */
+ public final String[] addresses;
+
+ /**
+ * This is the SSID reserved bits for each address
+ * This is necessary for proper repeater operation
+ */
+ public final byte[] ssidBits;
+
+ /**
+ * Index into addresses to last transmitted (default is 1)
+ */
+ public final int digied;
+
+ /**
+ * This is the packet data starting with the control field
+ */
+ public final byte[] restOfPacket;
+
+ /**
+ * Create straight from AX.25 format packet
+ */
+ AX25Packet(byte rcvd, byte[] pkt, int start)
+ {
+ this.rcvd = rcvd;
+
+ nsVector tpath = new nsVector();
+ nsVector tssids = new nsVector();
+ int p = start;
+
+ // Destination
+ for(int i=p;i<p+6;i++)
+ {
+ pkt[i] = (byte)(pkt[i]>>>1 & 0x7f);
+ }
+ String tstr = new String(pkt, 0, p, 6).trim();
+ p+=6;
+ tssids.addElement(new Integer(pkt[p]));
+ int ssid = (pkt[p++] & 0x1e)>>>1;
+ if (ssid != 0)
+ tpath.addElement(tstr+'-'+ssid);
+ else
+ tpath.addElement(tstr);
+
+ // Source
+ for(int i=p;i<p+6;i++)
+ {
+ pkt[i] = (byte)(pkt[i]>>>1 & 0x7f);
+ }
+ tstr = new String(pkt, 0, p, 6).trim();
+ p+=6;
+ tssids.addElement(new Integer(pkt[p]));
+ ssid = (pkt[p++] & 0x1e)>>>1;
+ if (ssid != 0)
+ tpath.addElement(tstr+'-'+ssid);
+ else
+ tpath.addElement(tstr);
+
+ // Path
+ int digi = 1;
+ while((pkt[p-1] & 0x1) != 1)
+ {
+ for(int i=p;i<p+6;i++)
+ {
+ pkt[i] = (byte)(pkt[i]>>>1 & 0x7f);
+ }
+ tstr = new String(pkt, 0, p, 6).trim();
+ p+=6;
+ byte tssid = pkt[p++];
+ ssid = (tssid & 0x1e)>>>1;
+ tssids.addElement(new Integer(tssid));
+ if((tssid & 0x80) == 0x80) digi = tpath.size();
+ if (ssid != 0)
+ tpath.addElement(tstr+'-'+ssid);
+ else
+ tpath.addElement(tstr);
+ }
+
+ restOfPacket = new byte[pkt.length - p];
+ System.arraycopy(pkt, p, restOfPacket, 0, pkt.length-p);
+
+ addresses = new String[tpath.size()];
+ tpath.copyInto(addresses);
+
+ ssidBits = new byte[tssids.size()];
+ for (int i = 0; i < ssidBits.length; i++)
+ {
+ ssidBits[i] = (byte)(((Integer)tssids.elementAt(i)).intValue()&0x60);
+ }
+ digied = digi;
+ }
+
+ /**
+ * Create straight from AX.25 format packet
+ */
+ AX25Packet(byte rcvd, byte[] pkt)
+ {
+ this(rcvd, pkt, 0);
+ }
+
+ /**
+ * Intialize from already parsed packet
+ */
+ AX25Packet(byte rcvd, String[] addresses, byte[] ssidBits, int digied, byte[] restOfPacket)
+ {
+ this.rcvd = rcvd;
+ this.addresses = addresses;
+ this.ssidBits = ssidBits;
+ this.digied = digied;
+ this.restOfPacket = restOfPacket;
+ }
+
+ /**
+ * @return AX.25 Format packet; null if not AX.25 packet
+ */
+ public final byte[] getAX25Packet()
+ {
+ int alen = addresses.length*7;
+ byte[] tpack = new byte[alen + restOfPacket.length];
+ if (!getAddress(addresses[0], ssidBits[0], tpack, 0)) return null;
+ if (!getAddress(addresses[1], ssidBits[1], tpack, 7)) return null;
+ for (int i = 2; i < addresses.length; i++)
+ {
+ if (!getAddress(addresses[i], ssidBits[i], tpack, i*7)) return null;
+ if (i <= digied) tpack[i*7+6] |= (byte)0x80;
+ }
+ tpack[alen - 1] |= 0x01;
+ System.arraycopy(restOfPacket, 0, tpack, alen, restOfPacket.length);
+ return tpack;
+ }
+
+ private final boolean getAddress(String strAddress, byte ssidbits, byte[] packet, int addressndx)
+ {
+ int da = strAddress.lastIndexOf('-');
+ if (da < 0) da = strAddress.length();
+ if (da > 6) return false; // Invalid callsign length
+ for(int i=0;i<da;i++)
+ { // Set call
+ packet[addressndx++] = (byte)(strAddress.charAt(i)<<1);
+ }
+ for(int i=da;i<6;i++)
+ { // Fill empty spots
+ packet[addressndx++] = 0x40;
+ }
+ switch (strAddress.length()-da)
+ {
+ case 2: // single digit
+ packet[addressndx] = (byte)(ssidbits | (strAddress.charAt(da+1)-0x30)<<1);
+ break;
+ case 3: // double digit
+ packet[addressndx] = (byte)(ssidbits | (strAddress.charAt(da+2)-0x30+10)<<1);
+ break;
+ default: // no SSID
+ packet[addressndx] = (byte)(ssidbits);
+ }
+ return true;
+ }
+
+ /**
+ * @return TNC2 Format packet with rcvd prepended; null if not APRS packet
+ */
+ public final byte[] getAPRSPacket()
+ {
+ if (restOfPacket.length <= 2
+ || restOfPacket[0] != 0x03
+ || restOfPacket[1] != (byte)0xf0) return null;
+ nsByteArrayOutputStream bpkt = new nsByteArrayOutputStream(restOfPacket.length+addresses.length*10+3);
+ DataOutputStream dpkt = new DataOutputStream(bpkt);
+ try
+ {
+ bpkt.write(rcvd);
+ dpkt.writeBytes(addresses[1]);
+ bpkt.write('>');
+ dpkt.writeBytes(addresses[0]);
+ for (int i = 2; i < addresses.length; i++)
+ {
+ bpkt.write(',');
+ dpkt.writeBytes(addresses[i]);
+ if (i == digied) dpkt.writeByte('*');
+ }
+ bpkt.write(':');
+ bpkt.write(restOfPacket, 2, restOfPacket.length-2);
+ }
+ catch (IOException ie)
+ {
+ ie.printStackTrace();
+ }
+ return bpkt.toByteArray();
+ }
+
+ public final String toString()
+ {
+ nsStringBuffer sb = new nsStringBuffer("Src:").append(addresses[1]).append(" Dest:").append(addresses[0]);
+ if (addresses.length > 2)
+ sb.append(" Via:").append(addresses[2]);
+ if (digied > 1) sb.append('*');
+ for (int i = 3; i < addresses.length; i++)
+ {
+ sb.append(',').append(addresses[i]);
+ if (digied >= i) sb.append('*');
+ }
+ sb.append(" Rest of Packet Len:").append(restOfPacket.length);
+ return sb.toString();
+ }
+ }
+}
33 src/TNCProcMonitor.java
@@ -0,0 +1,33 @@
+import java.io.*;
+
+class TNCProcMonitor extends Thread
+{
+ private final InputStream procstream;
+ private final PrintStream outstream;
+
+ TNCProcMonitor(InputStream procstream, PrintStream outstream)
+ {
+ this.procstream = procstream;
+ this.outstream = outstream;
+ setName(procstream.toString() + "->" + outstream.toString());
+ start();
+ }
+
+ public void run()
+ {
+ try
+ {
+ for(;;)
+ {
+ int _ch = procstream.read();
+ if(_ch != -1)
+ outstream.print((char)_ch);
+ else break;
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
58 src/TNCQueue.java
@@ -0,0 +1,58 @@
+import java.util.*;
+import com.ae5pl.nsutil.*;
+
+final class TNCQueue
+{
+ private final nsVector queue = new nsVector();
+ private boolean queueEnabled = true;
+
+ /**
+ * This gets the next object on the queue.
+ *
+ * @return This is null if the queue is disabled.
+ */
+ final synchronized TNCInterface.AX25Packet getFromQueue()
+ {
+ if (queueEnabled)
+ {
+ if (queue.isEmpty())
+ {
+ try {wait();} catch (InterruptedException e){}
+ if (!queueEnabled)
+ return null;
+ }
+ TNCInterface.AX25Packet retval = (TNCInterface.AX25Packet)queue.firstElement();
+ queue.removeElementAt(0);
+ return retval;
+ }
+ return null;
+ }
+
+ /**
+ * Checks for whether the queue is enabled.
+ */
+ final synchronized boolean isEnabled()
+ {
+ return queueEnabled;
+ }
+
+ /**
+ * This puts the object on the queue.
+ *
+ * @param newObject If this is null, the queue is purged and shut down.
+ */
+ final synchronized void putOnQueue(TNCInterface.AX25Packet newObject)
+ {
+ if (queueEnabled)
+ {
+ if (newObject == null)
+ {
+ queue.removeAllElements();
+ queueEnabled = false;
+ }
+ else
+ queue.addElement(newObject);
+ notifyAll();
+ }
+ }
+}
36 src/TNCSerialInterface.java
@@ -0,0 +1,36 @@
+import java.util.*;
+import java.io.*;
+
+public interface TNCSerialInterface
+{
+ /**
+ * Returns version string
+ */
+ public abstract String getVersion();
+
+ /**
+ * Closes related input and output streams
+ */
+ public abstract void close() throws IOException;
+
+ /**
+ * Returns an InputStream object
+ *
+ * @return Interface specific InputStream
+ */
+ public abstract InputStream getInputStream() throws IOException;
+
+ /**
+ * Returns an OutputStream object
+ *
+ * @return Interface specific OutputStream
+ */
+ public abstract OutputStream getOutputStream() throws IOException;
+
+ /**
+ * Open the port. Throws IOException upon failure.
+ *
+ * @param config Interface specific properties
+ */
+ public abstract void open(Properties config) throws IOException;
+}
26 src/TNCWriteThread.java
@@ -0,0 +1,26 @@
+abstract class TNCWriteThread extends Thread
+{
+ final TNCQueue queue = new TNCQueue();
+
+ volatile static int TNCSpeed = 1200;
+ volatile static int TNCVias = 7;
+ volatile static int TNCIMax = 256;
+
+ /**
+ * Pace packets so TNC is not overrun
+ * @param ax25Length length of ax.25 frame, including FEND's and FCS
+ */
+ protected void pace(TNCInterface.AX25Packet f)
+ {
+ if (TNCSpeed >= 8)
+ {
+ try {sleep((f.addresses.length*7+f.restOfPacket.length+4)*1000/(TNCSpeed>>3));}
+ catch (Exception se){se.printStackTrace();}
+ }
+ }
+
+ /**
+ * Override with your operating code.
+ */
+ abstract public void run();
+}
464 src/sivantoledo/ax25/Afsk1200Demodulator.java
@@ -0,0 +1,464 @@
+/*
+ * Audio FSK modem for AX25 (1200 Baud, 1200/2200Hz).
+ *
+ * Copyright (C) Sivan Toledo, 2012
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package sivantoledo.ax25;
+
+import java.util.Arrays;
+
+public class Afsk1200Demodulator
+ implements PacketDemodulator
+ //implements HalfduplexSoundcardClient
+ {
+ private float[] td_filter;
+ private float[] cd_filter;
+
+ private int rate_index;
+
+ private int sample_rate;
+ //private int samples_per_bit;
+ private float samples_per_bit;
+ //private float[] u1, u2, x, f0_cos, f0_sin, f1_cos, f1_sin;
+ private float[] u1, x;
+ private float[] c0_real, c0_imag, c1_real, c1_imag;
+ private float[] diff;
+ //private float[] fdiff;
+ private float previous_fdiff;
+ private int f0_i=0, f1_i=0;
+ private int last_transition;
+ private int data, bitcount;
+
+ private int vox_countdown = 0;
+ private float vox_threshold = 0.1f;
+
+ private float phase_inc_f0, phase_inc_f1;
+ private float phase_inc_symbol;
+
+ private Packet packet; // received packet
+ private PacketHandler handler;
+
+ private static enum State {
+ WAITING,
+ JUST_SEEN_FLAG,
+ DECODING
+ };
+ private State state = State.WAITING;
+
+ //TransmitController transmit_controller;
+
+ private int filter_index;
+
+ private int emphasis;
+
+ private boolean interpolate = false;
+ private float interpolate_last;
+ private boolean interpolate_original;
+
+ /*
+ * Diagnostic variables for estimating packet quality
+ */
+
+ private int f0_period_count, f1_period_count;
+ private float f0_max, f1_min; // to collect average max, min in the filtered diff signal
+ private float f0_current_max, f1_current_min;
+ private float max_period_error;
+
+ private void statisticsInit() {
+ f0_period_count = 0;
+ f1_period_count = 0;
+ f0_max = 0.0f;
+ f1_min = 0.0f;
+ max_period_error = 0.0f;
+ }
+
+ private void statisticsFinalize() {
+ f0_max = f0_max / f0_period_count;
+ f1_min = f1_min / f1_period_count;
+ //System.out.printf("%ddB avg(f0_extremes)/avg(f1_extremes) = %.2f period_rel_err = %.02f\n",
+ // emphasis,f0_max / -f1_min, max_period_error);
+ }
+
+ public Afsk1200Demodulator(int sample_rate, int filter_length) throws Exception {
+ this(sample_rate,filter_length,6,null);
+ }
+
+ public Afsk1200Demodulator(int sample_rate, int filter_length, int emphasis, PacketHandler h) throws Exception {
+
+ if (sample_rate==8000) {
+ interpolate = true;
+ sample_rate = 16000;
+ }
+
+ this.emphasis = emphasis;
+ //transmit_controller = c;
+ for (rate_index=0; rate_index<Afsk1200Filters.sample_rates.length; rate_index++) {
+ if (Afsk1200Filters.sample_rates[rate_index] == sample_rate) break;
+ }
+ if (rate_index == Afsk1200Filters.sample_rates.length) {
+ throw new Exception("Sample rate "+sample_rate+" not supported");
+ }
+
+ handler = h;
+ this.sample_rate = sample_rate;
+ this.samples_per_bit = (float) sample_rate / 1200.0f;
+ System.err.printf("samples per bit = %.3f\n", this.samples_per_bit);
+ //this.samples_per_bit = Afsk1200Filters.bit_periods[rate_index]; // this needs to be computed locally
+
+ //if (samples_per_bit * 1200 != sample_rate) {
+ // throw new Exception("Sample rate must be divisible by 1200");
+ //}
+ //System.out.printf("%d samples per bit\n",samples_per_bit);
+ //x = new float[samples_per_bit];
+ //u1 = new float[samples_per_bit];
+ //u2 = new float[samples_per_bit];
+
+ float[][][] tdf;
+ switch (emphasis) {
+ case 0:
+ tdf = Afsk1200Filters.time_domain_filter_none;
+ break;
+ case 6:
+ tdf = Afsk1200Filters.time_domain_filter_full;
+ break;
+ default:
+ System.err.printf("Filter for de-emphasis of %ddB is not availabe, using 6dB\n",
+ emphasis);
+ tdf = Afsk1200Filters.time_domain_filter_full;
+ break;
+ }
+
+ for (filter_index=0; filter_index<tdf.length; filter_index++) {
+ System.err.printf("Available filter length %d\n",tdf[filter_index][rate_index].length);
+ if (filter_length == tdf[filter_index][rate_index].length) {
+ System.err.printf("Using filter length %d\n",filter_length);
+ break;
+ }
+ }
+ if (filter_index==tdf.length) {
+ filter_index=tdf.length-1;
+ System.err.printf("Filter length %d not supported, using length %d\n",
+ filter_length,
+ tdf[filter_index][rate_index].length);
+ }
+
+ td_filter = tdf[filter_index][rate_index];
+ cd_filter = Afsk1200Filters.corr_diff_filter[filter_index][rate_index];
+
+ //System.out.printf("filter lengths are %d and %d\n",td_filter.length,cd_filter.length);
+
+ x = new float[td_filter.length];
+ u1 = new float[td_filter.length];
+ //u2 = new float[samples_per_bit];
+
+ //f0_cos = new float[samples_per_bit]; // f0=1200Hz so we have exactly one cycle.
+ //f0_sin = new float[samples_per_bit];
+ //f1_cos = new float[6*samples_per_bit]; // f0=2200Hz so 11 cycles fit exactly in 6 bit periods.
+ //f1_sin = new float[6*samples_per_bit]; // otherwise we would have had to compute sin/cos
+
+ c0_real = new float[(int) Math.floor(samples_per_bit)];
+ c0_imag = new float[(int) Math.floor(samples_per_bit)];
+ c1_real = new float[(int) Math.floor(samples_per_bit)];
+ c1_imag = new float[(int) Math.floor(samples_per_bit)];
+
+ //diff = new float[samples_per_bit];
+ //fdiff = new float[samples_per_bit];
+ diff = new float[cd_filter.length];
+ //fdiff = new float[corr_diff_filter[rate_index].length]; // can be length 2
+
+ phase_inc_f0 = (float) (2.0*Math.PI*1200.0/sample_rate);
+ phase_inc_f1 = (float) (2.0*Math.PI*2200.0/sample_rate);
+ phase_inc_symbol = (float) (2.0*Math.PI*1200.0/sample_rate);
+ //time_inc = (float) (2.0*Math.PI*i/sample_rate);
+
+ //for (int i=0; i<6*samples_per_bit; i++) {
+ // float time = (float) (2.0*Math.PI*i/sample_rate);
+ // f1_cos[i] = (float) Math.cos(2200.0*time);
+ // f1_sin[i] = (float) Math.sin(2200.0*time);
+ // if (i>=samples_per_bit) continue;
+ // f0_cos[i] = (float) Math.cos(1200.0*time);
+ // f0_sin[i] = (float) Math.sin(1200.0*time);
+ //}
+
+ //System.out.printf("Size of symbol sync filter is %d\n", symbol_sync_filter.length);
+ }
+
+ private volatile boolean data_carrier = false;
+ public boolean dcd() { return data_carrier; }
+
+ private float correlation(float[] x, float[] y, int j) {
+ float c = (float) 0.0;
+ for (int i=0; i<x.length; i++) {
+ c += x[j]*y[j];
+ j--;
+ if (j==-1) j=x.length - 1;
+ }
+ return c;
+ }
+
+ private float sum(float[] x, int j) {
+ float c = (float) 0.0;
+ for (int i=0; i<x.length; i++) {
+ c += x[j];
+ j--;
+ if (j==-1) j=x.length - 1;
+ }
+ return c;
+ }
+
+ private int j_td; // time domain index
+ private int j_cd; // time domain index
+ private int j_corr; // correlation index
+
+ private float phase_f0, phase_f1;
+
+ //private int j; // sample index, rolls over each bit period
+ //private int j_f1; // sample index
+ private int t; // running sample counter
+
+ private float f1cos, f1sin, f0cos, f0sin;
+
+ //public void addSamples(float[] s) {
+ // addSamples(s,s.length);
+ //}
+
+ private int flag_count = 0;
+ private boolean flag_separator_seen = false; // to process the single-bit separation period between flags
+
+ private int decode_count = 0;
+
+ private boolean vox_state = false;
+
+ public void addSamples(float[] s, int n) {
+ //for (int i=0; i<n; i++) {
+ int i = 0;
+ while (i<n) {
+ float sample;
+ if (interpolate) {
+ if (interpolate_original) {
+ sample = s[i];
+ interpolate_last = sample;
+ interpolate_original = false;
+ i++;
+ } else {
+ sample = 0.5f * (s[i] + interpolate_last);
+ interpolate_original = true;
+ }
+ } else {
+ sample = s[i];
+ i++;
+ }
+
+ //if (sample > vox_threshold || sample < -vox_threshold) {
+ // vox_countdown = sample_rate; // 1s lingering
+ // if (vox_state==false)
+ // System.err.println("vox activating");
+ // vox_state = true;
+ //}
+
+ //if (vox_countdown == 0) {
+ // if (vox_state==true)
+ // System.err.println("vox deactivating");
+ // vox_state = false;
+ // continue;
+ //} else vox_countdown--;
+
+ u1[j_td]= sample;
+ //u1[j_td]= s[i];
+ //u2[j] = Filter.filter(u1, j, Filter.BANDPASS_1150_1250_48000_39);
+ //x[j] = Filter.filter(u2, j, Filter.BANDPASS_2150_2250_48000_39);
+ //u2[j] = Filter.filter(u1, j, Filter.BANDPASS_1150_1250_48000_39);
+ x[j_td] = Filter.filter(u1, j_td, td_filter);
+
+ // compute correlation running value
+ //c0_real[j] = x[j_td]*f0_cos[j];
+ //c0_imag[j] = x[j_td]*f0_sin[j];
+ //
+ //c1_real[j] = x[j_td]*f1_cos[j_f1];
+ //c1_imag[j] = x[j_td]*f1_sin[j_f1];
+
+ c0_real[j_corr] = x[j_td]*(float) Math.cos(phase_f0);
+ c0_imag[j_corr] = x[j_td]*(float) Math.sin(phase_f0);
+
+ c1_real[j_corr] = x[j_td]*(float) Math.cos(phase_f1);
+ c1_imag[j_corr] = x[j_td]*(float) Math.sin(phase_f1);
+
+ phase_f0 += phase_inc_f0; if (phase_f0 > (float) 2.0*Math.PI) phase_f0 -= (float) 2.0*Math.PI;
+ phase_f1 += phase_inc_f1; if (phase_f1 > (float) 2.0*Math.PI) phase_f1 -= (float) 2.0*Math.PI;
+
+ float cr = sum(c0_real,j_corr);
+ float ci = sum(c0_imag,j_corr);
+ float c0 = (float) Math.sqrt(cr*cr + ci*ci);
+
+ cr = sum(c1_real,j_corr);
+ ci = sum(c1_imag,j_corr);
+ float c1 = (float) Math.sqrt(cr*cr + ci*ci);
+
+ //diff[j_corr] = c0-c1;
+ diff[j_cd] = c0-c1;
+ //fdiff[j_corr] = Filter.filter(diff,j_corr,Filter.LOWPASS_1200_48000_39);
+ //float fdiff = Filter.filter(diff,j_corr,cd_filter);
+ float fdiff = Filter.filter(diff,j_cd,cd_filter);
+
+ //System.out.printf("%d %f %f : ",j,diff[j],fdiff[j]);
+ //System.out.printf("%d %f %f %f %f : ",j,f0_cos[j],f0_sin[j],f1_cos[j_f1],f1_sin[j_f1]);
+
+ //float previous_fdiff = (j_corr==0) ? fdiff[fdiff.length-1] : fdiff[j_corr-1];
+ //if (previous_fdiff*fdiff[j_corr] < 0 || previous_fdiff==0) {
+ if (previous_fdiff*fdiff < 0 || previous_fdiff==0) {
+
+ // we found a transition
+ int p = t - last_transition;
+ last_transition = t;
+
+ int bits = (int) Math.round((double) p / (double)samples_per_bit);
+ //System.out.printf("$ %f %d\n",(double) p / (double)samples_per_bit,bits);
+
+ // collect statistics
+ if (fdiff < 0) { // last period was high, meaning f0
+ f0_period_count++;
+ f0_max += f0_current_max;
+ double err = Math.abs(bits - ((double) p / (double)samples_per_bit));
+ //System.out.printf(")) %.02f %d %.02f\n",(double) p / (double)samples_per_bit,bits,err);
+ if (err > max_period_error) max_period_error = (float) err;
+
+ // prepare for the period just starting now
+ f1_current_min = fdiff;
+ } else {
+ f1_period_count++;
+ f1_min += f1_current_min;
+ double err = Math.abs(bits - ((double) p / (double)samples_per_bit));
+ //System.out.printf(")) %.02f %d %.02f\n",(double) p / (double)samples_per_bit,bits,err);
+ if (err > max_period_error) max_period_error = (float) err;
+
+ // prepare for the period just starting now
+ f0_current_max = fdiff;
+ }
+
+ if (bits==0 || bits>7) {
+ state=State.WAITING;
+ data_carrier = false;
+ flag_count = 0;
+ } else {
+ if (bits==7) {
+ flag_count++;
+ flag_separator_seen=false;
+ //System.out.printf("Seen %d flags in a row\n",flag_count);
+
+ data = 0;
+ bitcount = 0;
+ switch (state) {
+ case WAITING:
+ state=State.JUST_SEEN_FLAG;
+ data_carrier = true;
+
+ statisticsInit(); // start measuring a new packet
+ break;
+ case JUST_SEEN_FLAG:
+ break;
+ case DECODING:
+ if (packet!=null && packet.terminate()) {
+ statisticsFinalize();
+ packet.statistics(new float[] {emphasis,f0_max/-f1_min,max_period_error});
+ //System.out.print(String.format("%ddB:%.02f:%.02f\n",
+ // emphasis,f0_max/-f1_min,max_period_error));
+ if (handler!=null)
+ handler.handlePacket(packet.bytesWithoutCRC());
+ else
+ System.out.println(""+(++decode_count)+": "+packet);
+ }
+ packet = null;
+ state=State.JUST_SEEN_FLAG;
+ break;
+ }
+ } else {
+ switch (state) {
+ case WAITING:
+ break;
+ case JUST_SEEN_FLAG:
+ state=State.DECODING;
+ break;
+ case DECODING:
+ break;
+ }
+ if (state==State.DECODING) {
+ if (bits != 1) {
+ flag_count = 0;
+ } else {
+ if (flag_count>0 && !flag_separator_seen) flag_separator_seen=true;
+ else {
+ flag_count = 0;
+ }
+ }
+
+ for (int k=0; k<bits-1; k++) {
+ bitcount++;
+ data >>= 1;
+ data += 128;
+ if (bitcount==8) {
+ if (packet==null) packet = new Packet();
+ //if (data==0xAA) packet.terminate();
+ if (!packet.addByte((byte) data)) {
+ state=State.WAITING;
+ data_carrier = false;
+ }
+ //System.out.printf(">>> %02x %c %c\n", data, (char)data, (char)(data>>1));
+ data = 0;
+ bitcount = 0;
+ }
+ }
+ if (bits-1 != 5) { // the zero after the ones is not a stuffing
+ bitcount++;
+ data >>= 1;
+ if (bitcount==8) {
+ if (packet==null) packet = new Packet();
+ //if (data==0xAA) packet.terminate();
+ if (!packet.addByte((byte) data)) {
+ state=State.WAITING;
+ data_carrier = false;
+ }
+ //System.out.printf(">>> %02x %c %c\n", data, (char)data, (char)(data>>1));
+ data = 0;
+ bitcount = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ previous_fdiff = fdiff;
+
+ t++;
+
+ j_td++;
+ if (j_td==td_filter.length) j_td=0;
+
+ j_cd++;
+ if (j_cd==cd_filter.length) j_cd=0;
+
+ j_corr++;
+ if (j_corr==c0_real.length /* samples_per_bit*/) j_corr=0;
+
+ //j++;
+ //if (j==samples_per_bit) j=0;
+
+ //j_f1++;
+ //if (j_f1==6*samples_per_bit) j_f1=0;
+ }
+ }
+}
71 src/sivantoledo/ax25/Afsk1200Filters.java
@@ -0,0 +1,71 @@
+package sivantoledo.ax25;
+public class Afsk1200Filters {
+ static final int[] sample_rates = { 9600, 12000, 11025, 16000, 22050, 24000, 44100, 48000 };
+ static final int[] bit_periods = { 8, 10, 9, 13, 18, 20, 36, 40 };
+ static final float[][][] time_domain_filter_full = {
+ {
+ { -8.538057e-002f, -1.606386e-001f, -2.562661e-002f, 1.780909e-001f, 1.780909e-001f, -2.562661e-002f, -1.606386e-001f, -8.538057e-002f },
+ { -5.622111e-002f, -1.260336e-001f, -9.508627e-002f, 3.338428e-002f, 1.523783e-001f, 1.523783e-001f, 3.338428e-002f, -9.508627e-002f, -1.260336e-001f, -5.622111e-002f },
+ { -7.586213e-002f, -1.412157e-001f, -6.605851e-002f, 9.922325e-002f, 1.856153e-001f, 9.922325e-002f, -6.605851e-002f, -1.412157e-001f, -7.586213e-002f },
+ { -4.165569e-002f, -8.654256e-002f, -9.469657e-002f, -5.238007e-002f, 2.472920e-002f, 9.802254e-002f, 1.279778e-001f, 9.802254e-002f, 2.472920e-002f, -5.238007e-002f, -9.469657e-002f, -8.654256e-002f, -4.165569e-002f },
+ { -2.364670e-002f, -5.043069e-002f, -6.796924e-002f, -6.863212e-002f, -4.951926e-002f, -1.398200e-002f, 2.897697e-002f, 6.733815e-002f, 8.988417e-002f, 8.988417e-002f, 6.733815e-002f, 2.897697e-002f, -1.398200e-002f, -4.951926e-002f, -6.863212e-002f, -6.796924e-002f, -5.043069e-002f, -2.364670e-002f },
+ { -1.595292e-002f, -3.931281e-002f, -5.770766e-002f, -6.496202e-002f, -5.733060e-002f, -3.487383e-002f, -1.778508e-003f, 3.454271e-002f, 6.535965e-002f, 8.300178e-002f, 8.300178e-002f, 6.535965e-002f, 3.454271e-002f, -1.778508e-003f, -3.487383e-002f, -5.733060e-002f, -6.496202e-002f, -5.770766e-002f, -3.931281e-002f, -1.595292e-002f },
+ { -8.261884e-003f, -1.530739e-002f, -2.208928e-002f, -2.799960e-002f, -3.245890e-002f, -3.497078e-002f, -3.517137e-002f, -3.286900e-002f, -2.806984e-002f, -2.098659e-002f, -1.202899e-002f, -1.776253e-003f, 9.066339e-003f, 1.972315e-002f, 2.941143e-002f, 3.740638e-002f, 4.310223e-002f, 4.606384e-002f, 4.606384e-002f, 4.310223e-002f, 3.740638e-002f, 2.941143e-002f, 1.972315e-002f, 9.066339e-003f, -1.776253e-003f, -1.202899e-002f, -2.098659e-002f, -2.806984e-002f, -3.286900e-002f, -3.517137e-002f, -3.497078e-002f, -3.245890e-002f, -2.799960e-002f, -2.208928e-002f, -1.530739e-002f, -8.261884e-003f },
+ { -5.028936e-003f, -1.090705e-002f, -1.680680e-002f, -2.230031e-002f, -2.695781e-002f, -3.038067e-002f, -3.223361e-002f, -3.227344e-002f, -3.037178e-002f, -2.652998e-002f, -2.088457e-002f, -1.370266e-002f, -5.367345e-003f, 3.646094e-003f, 1.280138e-002f, 2.153775e-002f, 2.930895e-002f, 3.562187e-002f, 4.007171e-002f, 4.237116e-002f, 4.237116e-002f, 4.007171e-002f, 3.562187e-002f, 2.930895e-002f, 2.153775e-002f, 1.280138e-002f, 3.646094e-003f, -5.367345e-003f, -1.370266e-002f, -2.088457e-002f, -2.652998e-002f, -3.037178e-002f, -3.227344e-002f, -3.223361e-002f, -3.038067e-002f, -2.695781e-002f, -2.230031e-002f, -1.680680e-002f, -1.090705e-002f, -5.028936e-003f }
+ },
+ {
+ { -2.185257e-002f, -6.703124e-003f, 6.574072e-002f, 6.194076e-002f, -7.181203e-002f, -1.592129e-001f, -3.565027e-002f, 1.616405e-001f, 1.616405e-001f, -3.565027e-002f, -1.592129e-001f, -7.181203e-002f, 6.194076e-002f, 6.574072e-002f, -6.703124e-003f, -2.185257e-002f },
+ { -1.512995e-002f, -1.638177e-002f, 2.300329e-002f, 6.330897e-002f, 4.257491e-002f, -4.503183e-002f, -1.218371e-001f, -9.857633e-002f, 2.362166e-002f, 1.391256e-001f, 1.391256e-001f, 2.362166e-002f, -9.857633e-002f, -1.218371e-001f, -4.503183e-002f, 4.257491e-002f, 6.330897e-002f, 2.300329e-002f, -1.638177e-002f, -1.512995e-002f },
+ { -2.064753e-002f, -7.677662e-003f, 4.760857e-002f, 6.827848e-002f, -6.116157e-003f, -1.150274e-001f, -1.245511e-001f, 5.302535e-003f, 1.482799e-001f, 1.482799e-001f, 5.302535e-003f, -1.245511e-001f, -1.150274e-001f, -6.116157e-003f, 6.827848e-002f, 4.760857e-002f, -7.677662e-003f, -2.064753e-002f },
+ { -1.279020e-002f, -1.401851e-002f, 3.287701e-003f, 3.091304e-002f, 4.859587e-002f, 3.723111e-002f, -5.806838e-003f, -6.065027e-002f, -9.437141e-002f, -8.135381e-002f, -2.153914e-002f, 5.640279e-002f, 1.105078e-001f, 1.105078e-001f, 5.640279e-002f, -2.153914e-002f, -8.135381e-002f, -9.437141e-002f, -6.065027e-002f, -5.806838e-003f, 3.723111e-002f, 4.859587e-002f, 3.091304e-002f, 3.287701e-003f, -1.401851e-002f, -1.279020e-002f },
+ { -7.792168e-003f, -1.135158e-002f, -8.224556e-003f, 2.012026e-003f, 1.651861e-002f, 2.980923e-002f, 3.560486e-002f, 2.931803e-002f, 1.022578e-002f, -1.759227e-002f, -4.607374e-002f, -6.562544e-002f, -6.842752e-002f, -5.141378e-002f, -1.778817e-002f, 2.358239e-002f, 6.080124e-002f, 8.274861e-002f, 8.274861e-002f, 6.080124e-002f, 2.358239e-002f, -1.778817e-002f, -5.141378e-002f, -6.842752e-002f, -6.562544e-002f, -4.607374e-002f, -1.759227e-002f, 1.022578e-002f, 2.931803e-002f, 3.560486e-002f, 2.980923e-002f, 1.651861e-002f, 2.012026e-003f, -8.224556e-003f, -1.135158e-002f, -7.792168e-003f },
+ { -4.702692e-003f, -9.502095e-003f, -1.005093e-002f, -4.953291e-003f, 5.202632e-003f, 1.771241e-002f, 2.837769e-002f, 3.275849e-002f, 2.769259e-002f, 1.258595e-002f, -9.994799e-003f, -3.470396e-002f, -5.474560e-002f, -6.380472e-002f, -5.801069e-002f, -3.731193e-002f, -5.791305e-003f, 2.923712e-002f, 5.913253e-002f, 7.629442e-002f, 7.629442e-002f, 5.913253e-002f, 2.923712e-002f, -5.791305e-003f, -3.731193e-002f, -5.801069e-002f, -6.380472e-002f, -5.474560e-002f, -3.470396e-002f, -9.994799e-003f, 1.258595e-002f, 2.769259e-002f, 3.275849e-002f, 2.837769e-002f, 1.771241e-002f, 5.202632e-003f, -4.953291e-003f, -1.005093e-002f, -9.502095e-003f, -4.702692e-003f },
+ { -3.076732e-003f, -4.580748e-003f, -5.498025e-003f, -5.631505e-003f, -4.856768e-003f, -3.142627e-003f, -5.631590e-004f, 2.700709e-003f, 6.370970e-003f, 1.009341e-002f, 1.346813e-002f, 1.608716e-002f, 1.757579e-002f, 1.763350e-002f, 1.607036e-002f, 1.283499e-002f, 8.030552e-003f, 1.916793e-003f, -5.103076e-003f, -1.251007e-002f, -1.970977e-002f, -2.608200e-002f, -3.103558e-002f, -3.406302e-002f, -3.479015e-002f, -3.301564e-002f, -2.873649e-002f, -2.215653e-002f, -1.367671e-002f, -3.867528e-003f, 6.574365e-003f, 1.688170e-002f, 2.627951e-002f, 3.404991e-002f, 3.959275e-002f, 4.247681e-002f, 4.247681e-002f, 3.959275e-002f, 3.404991e-002f, 2.627951e-002f, 1.688170e-002f, 6.574365e-003f, -3.867528e-003f, -1.367671e-002f, -2.215653e-002f, -2.873649e-002f, -3.301564e-002f, -3.479015e-002f, -3.406302e-002f, -3.103558e-002f, -2.608200e-002f, -1.970977e-002f, -1.251007e-002f, -5.103076e-003f, 1.916793e-003f, 8.030552e-003f, 1.283499e-002f, 1.607036e-002f, 1.763350e-002f, 1.757579e-002f, 1.608716e-002f, 1.346813e-002f, 1.009341e-002f, 6.370970e-003f, 2.700709e-003f, -5.631590e-004f, -3.142627e-003f, -4.856768e-003f, -5.631505e-003f, -5.498025e-003f, -4.580748e-003f, -3.076732e-003f },
+ { -1.555936e-003f, -3.074922e-003f, -4.298532e-003f, -5.055988e-003f, -5.207867e-003f, -4.661980e-003f, -3.386024e-003f, -1.415645e-003f, 1.143128e-003f, 4.117646e-003f, 7.278662e-003f, 1.035612e-002f, 1.305964e-002f, 1.510224e-002f, 1.622537e-002f, 1.622320e-002f, 1.496402e-002f, 1.240675e-002f, 8.611013e-003f, 3.739340e-003f, -1.948784e-003f, -8.111153e-003f, -1.434449e-002f, -2.021209e-002f, -2.527549e-002f, -2.912772e-002f, -3.142564e-002f, -3.191868e-002f, -3.047148e-002f, -2.707872e-002f, -2.187043e-002f, -1.510729e-002f, -7.165972e-003f, 1.484604e-003f, 1.031443e-002f, 1.876872e-002f, 2.630674e-002f, 3.244019e-002f, 3.676812e-002f, 3.900592e-002f, 3.900592e-002f, 3.676812e-002f, 3.244019e-002f, 2.630674e-002f, 1.876872e-002f, 1.031443e-002f, 1.484604e-003f, -7.165972e-003f, -1.510729e-002f, -2.187043e-002f, -2.707872e-002f, -3.047148e-002f, -3.191868e-002f, -3.142564e-002f, -2.912772e-002f, -2.527549e-002f, -2.021209e-002f, -1.434449e-002f, -8.111153e-003f, -1.948784e-003f, 3.739340e-003f, 8.611013e-003f, 1.240675e-002f, 1.496402e-002f, 1.622320e-002f, 1.622537e-002f, 1.510224e-002f, 1.305964e-002f, 1.035612e-002f, 7.278662e-003f, 4.117646e-003f, 1.143128e-003f, -1.415645e-003f, -3.386024e-003f, -4.661980e-003f, -5.207867e-003f, -5.055988e-003f, -4.298532e-003f, -3.074922e-003f, -1.555936e-003f }
+ }
+ };
+ static final float[][][] time_domain_filter_none = {
+ {
+ { -1.339009e-001f, -2.058943e-001f, -1.939428e-002f, 2.397803e-001f, 2.397803e-001f, -1.939428e-002f, -2.058943e-001f, -1.339009e-001f },
+ { -9.317707e-002f, -1.674304e-001f, -1.144164e-001f, 5.382636e-002f, 2.042595e-001f, 2.042595e-001f, 5.382636e-002f, -1.144164e-001f, -1.674304e-001f, -9.317707e-002f },
+ { -1.178083e-001f, -1.834575e-001f, -7.457574e-002f, 1.382653e-001f, 2.471472e-001f, 1.382653e-001f, -7.457574e-002f, -1.834575e-001f, -1.178083e-001f },
+ { -6.883035e-002f, -1.183793e-001f, -1.202390e-001f, -6.050761e-002f, 3.973044e-002f, 1.327173e-001f, 1.703936e-001f, 1.327173e-001f, 3.973044e-002f, -6.050761e-002f, -1.202390e-001f, -1.183793e-001f, -6.883035e-002f },
+ { -4.227421e-002f, -7.290506e-002f, -9.066040e-002f, -8.705643e-002f, -5.936009e-002f, -1.225971e-002f, 4.294760e-002f, 9.153592e-002f, 1.198955e-001f, 1.198955e-001f, 9.153592e-002f, 4.294760e-002f, -1.225971e-002f, -5.936009e-002f, -8.705643e-002f, -9.066040e-002f, -7.290506e-002f, -4.227421e-002f },
+ { -3.222841e-002f, -5.937048e-002f, -7.900318e-002f, -8.418689e-002f, -7.099170e-002f, -4.003098e-002f, 3.238984e-003f, 4.963661e-002f, 8.853456e-002f, 1.106705e-001f, 1.106705e-001f, 8.853456e-002f, 4.963661e-002f, 3.238984e-003f, -4.003098e-002f, -7.099170e-002f, -8.418689e-002f, -7.900318e-002f, -5.937048e-002f, -3.222841e-002f },
+ { -1.688675e-002f, -2.522894e-002f, -3.298569e-002f, -3.945459e-002f, -4.397690e-002f, -4.600012e-002f, -4.513445e-002f, -4.119757e-002f, -3.424285e-002f, -2.456778e-002f, -1.270102e-002f, 6.314195e-004f, 1.455858e-002f, 2.813110e-002f, 4.039641e-002f, 5.047587e-002f, 5.763707e-002f, 6.135475e-002f, 6.135475e-002f, 5.763707e-002f, 5.047587e-002f, 4.039641e-002f, 2.813110e-002f, 1.455858e-002f, 6.314195e-004f, -1.270102e-002f, -2.456778e-002f, -3.424285e-002f, -4.119757e-002f, -4.513445e-002f, -4.600012e-002f, -4.397690e-002f, -3.945459e-002f, -3.298569e-002f, -2.522894e-002f, -1.688675e-002f },
+ { -1.254927e-002f, -1.960191e-002f, -2.645696e-002f, -3.261764e-002f, -3.759252e-002f, -4.093339e-002f, -4.227201e-002f, -4.135251e-002f, -3.805685e-002f, -3.242095e-002f, -2.463990e-002f, -1.506147e-002f, -4.167957e-003f, 7.452467e-003f, 1.914302e-002f, 3.022160e-002f, 4.002689e-002f, 4.796383e-002f, 5.354507e-002f, 5.642521e-002f, 5.642521e-002f, 5.354507e-002f, 4.796383e-002f, 4.002689e-002f, 3.022160e-002f, 1.914302e-002f, 7.452467e-003f, -4.167957e-003f, -1.506147e-002f, -2.463990e-002f, -3.242095e-002f, -3.805685e-002f, -4.135251e-002f, -4.227201e-002f, -4.093339e-002f, -3.759252e-002f, -3.261764e-002f, -2.645696e-002f, -1.960191e-002f, -1.254927e-002f }
+ },
+ {
+ { -2.084545e-003f, 2.254878e-002f, 7.962869e-002f, 3.712606e-002f, -1.261127e-001f, -2.000219e-001f, -2.410548e-002f, 2.206759e-001f, 2.206759e-001f, -2.410548e-002f, -2.000219e-001f, -1.261127e-001f, 3.712606e-002f, 7.962869e-002f, 2.254878e-002f, -2.084545e-003f },
+ { -6.220226e-004f, 6.246309e-003f, 4.336082e-002f, 6.631613e-002f, 1.945471e-002f, -8.739570e-002f, -1.618851e-001f, -1.124118e-001f, 4.660931e-002f, 1.883459e-001f, 1.883459e-001f, 4.660931e-002f, -1.124118e-001f, -1.618851e-001f, -8.739570e-002f, 1.945471e-002f, 6.631613e-002f, 4.336082e-002f, 6.246309e-003f, -6.220226e-004f },
+ { -2.395635e-003f, 1.813050e-002f, 6.408293e-002f, 5.727940e-002f, -4.612471e-002f, -1.622780e-001f, -1.473014e-001f, 2.517136e-002f, 2.005980e-001f, 2.005980e-001f, 2.517136e-002f, -1.473014e-001f, -1.622780e-001f, -4.612471e-002f, 5.727940e-002f, 6.408293e-002f, 1.813050e-002f, -2.395635e-003f },
+ { -1.236077e-003f, 2.548709e-003f, 2.082463e-002f, 4.296432e-002f, 4.876612e-002f, 2.229715e-002f, -3.371153e-002f, -9.400324e-002f, -1.225961e-001f, -9.462111e-002f, -1.448180e-002f, 8.254569e-002f, 1.481262e-001f, 1.481262e-001f, 8.254569e-002f, -1.448180e-002f, -9.462111e-002f, -1.225961e-001f, -9.400324e-002f, -3.371153e-002f, 2.229715e-002f, 4.876612e-002f, 4.296432e-002f, 2.082463e-002f, 2.548709e-003f, -1.236077e-003f },
+ { -2.348621e-004f, -8.188409e-004f, 4.322591e-003f, 1.472729e-002f, 2.692352e-002f, 3.529607e-002f, 3.407530e-002f, 1.978998e-002f, -6.762944e-003f, -3.990836e-002f, -7.028280e-002f, -8.760362e-002f, -8.415882e-002f, -5.779760e-002f, -1.328262e-002f, 3.861673e-002f, 8.412538e-002f, 1.106300e-001f, 1.106300e-001f, 8.412538e-002f, 3.861673e-002f, -1.328262e-002f, -5.779760e-002f, -8.415882e-002f, -8.760362e-002f, -7.028280e-002f, -3.990836e-002f, -6.762944e-003f, 1.978998e-002f, 3.407530e-002f, 3.529607e-002f, 2.692352e-002f, 1.472729e-002f, 4.322591e-003f, -8.188409e-004f, -2.348621e-004f },
+ { 1.235916e-003f, -1.014480e-003f, 5.589534e-004f, 6.739477e-003f, 1.638179e-002f, 2.643799e-002f, 3.270933e-002f, 3.116178e-002f, 1.941992e-002f, -2.058635e-003f, -2.957644e-002f, -5.679531e-002f, -7.626852e-002f, -8.151785e-002f, -6.905449e-002f, -3.971027e-002f, 1.183533e-003f, 4.490369e-002f, 8.147414e-002f, 1.022567e-001f, 1.022567e-001f, 8.147414e-002f, 4.490369e-002f, 1.183533e-003f, -3.971027e-002f, -6.905449e-002f, -8.151785e-002f, -7.626852e-002f, -5.679531e-002f, -2.957644e-002f, -2.058635e-003f, 1.941992e-002f, 3.116178e-002f, 3.270933e-002f, 2.643799e-002f, 1.638179e-002f, 6.739477e-003f, 5.589534e-004f, -1.014480e-003f, 1.235916e-003f },
+ { 3.050483e-004f, -4.120990e-004f, -5.782374e-004f, -5.135811e-005f, 1.234737e-003f, 3.254133e-003f, 5.881370e-003f, 8.894417e-003f, 1.198857e-002f, 1.480067e-002f, 1.694188e-002f, 1.803627e-002f, 1.776163e-002f, 1.588847e-002f, 1.231317e-002f, 7.081354e-003f, 3.986663e-004f, -7.373145e-003f, -1.573535e-002f, -2.408573e-002f, -3.176402e-002f, -3.810588e-002f, -4.250057e-002f, -4.444736e-002f, -4.360521e-002f, -3.983105e-002f, -3.320258e-002f, -2.402286e-002f, -1.280579e-002f, -2.429808e-004f, 1.284560e-002f, 2.557315e-002f, 3.705480e-002f, 4.647766e-002f, 5.316604e-002f, 5.663631e-002f, 5.663631e-002f, 5.316604e-002f, 4.647766e-002f, 3.705480e-002f, 2.557315e-002f, 1.284560e-002f, -2.429808e-004f, -1.280579e-002f, -2.402286e-002f, -3.320258e-002f, -3.983105e-002f, -4.360521e-002f, -4.444736e-002f, -4.250057e-002f, -3.810588e-002f, -3.176402e-002f, -2.408573e-002f, -1.573535e-002f, -7.373145e-003f, 3.986663e-004f, 7.081354e-003f, 1.231317e-002f, 1.588847e-002f, 1.776163e-002f, 1.803627e-002f, 1.694188e-002f, 1.480067e-002f, 1.198857e-002f, 8.894417e-003f, 5.881370e-003f, 3.254133e-003f, 1.234737e-003f, -5.135811e-005f, -5.782374e-004f, -4.120990e-004f, 3.050483e-004f },
+ { 1.090725e-003f, 2.145359e-004f, -3.671281e-004f, -5.168203e-004f, -1.324122e-004f, 8.390482e-004f, 2.391043e-003f, 4.452077e-003f, 6.885825e-003f, 9.497335e-003f, 1.204518e-002f, 1.425887e-002f, 1.586042e-002f, 1.658843e-002f, 1.622265e-002f, 1.460720e-002f, 1.167002e-002f, 7.436988e-003f, 2.038835e-003f, -4.289859e-003f, -1.122021e-002f, -1.834677e-002f, -2.521240e-002f, -3.133890e-002f, -3.626095e-002f, -3.956110e-002f, -4.090303e-002f, -4.006032e-002f, -3.693852e-002f, -3.158836e-002f, -2.420895e-002f, -1.514021e-002f, -4.844976e-003f, 6.118407e-003f, 1.713096e-002f, 2.755318e-002f, 3.676743e-002f, 4.421957e-002f, 4.945669e-002f, 5.215826e-002f, 5.215826e-002f, 4.945669e-002f, 4.421957e-002f, 3.676743e-002f, 2.755318e-002f, 1.713096e-002f, 6.118407e-003f, -4.844976e-003f, -1.514021e-002f, -2.420895e-002f, -3.158836e-002f, -3.693852e-002f, -4.006032e-002f, -4.090303e-002f, -3.956110e-002f, -3.626095e-002f, -3.133890e-002f, -2.521240e-002f, -1.834677e-002f, -1.122021e-002f, -4.289859e-003f, 2.038835e-003f, 7.436988e-003f, 1.167002e-002f, 1.460720e-002f, 1.622265e-002f, 1.658843e-002f, 1.586042e-002f, 1.425887e-002f, 1.204518e-002f, 9.497335e-003f, 6.885825e-003f, 4.452077e-003f, 2.391043e-003f, 8.390482e-004f, -1.324122e-004f, -5.168203e-004f, -3.671281e-004f, 2.145359e-004f, 1.090725e-003f }
+ }
+ };
+ static final float[][][] corr_diff_filter = {
+ {
+ { 3.560173e-003f, 3.808372e-002f, 1.610319e-001f, 2.973243e-001f, 2.973243e-001f, 1.610319e-001f, 3.808372e-002f, 3.560173e-003f },
+ { 2.199047e-003f, 1.735971e-002f, 7.367287e-002f, 1.662386e-001f, 2.405298e-001f, 2.405298e-001f, 1.662386e-001f, 7.367287e-002f, 1.735971e-002f, 2.199047e-003f },
+ { 3.225875e-003f, 2.591391e-002f, 1.079899e-001f, 2.232384e-001f, 2.792639e-001f, 2.232384e-001f, 1.079899e-001f, 2.591391e-002f, 3.225875e-003f },
+ { 1.649749e-003f, 8.019771e-003f, 2.951243e-002f, 7.118514e-002f, 1.247140e-001f, 1.705763e-001f, 1.886853e-001f, 1.705763e-001f, 1.247140e-001f, 7.118514e-002f, 2.951243e-002f, 8.019771e-003f, 1.649749e-003f },
+ { 8.636339e-004f, 3.182517e-003f, 9.639032e-003f, 2.284951e-002f, 4.353592e-002f, 6.976907e-002f, 9.715855e-002f, 1.199990e-001f, 1.330028e-001f, 1.330028e-001f, 1.199990e-001f, 9.715855e-002f, 6.976907e-002f, 4.353592e-002f, 2.284951e-002f, 9.639032e-003f, 3.182517e-003f, 8.636339e-004f },
+ { 5.110546e-004f, 2.174060e-003f, 6.473724e-003f, 1.533688e-002f, 2.975306e-002f, 4.921456e-002f, 7.157931e-002f, 9.343798e-002f, 1.109054e-001f, 1.206140e-001f, 1.206140e-001f, 1.109054e-001f, 9.343798e-002f, 7.157931e-002f, 4.921456e-002f, 2.975306e-002f, 1.533688e-002f, 6.473724e-003f, 2.174060e-003f, 5.110546e-004f },
+ { 2.639688e-004f, 6.466073e-004f, 1.289839e-003f, 2.384937e-003f, 4.112716e-003f, 6.622334e-003f, 1.001174e-002f, 1.431186e-002f, 1.947623e-002f, 2.537743e-002f, 3.181093e-002f, 3.850630e-002f, 4.514497e-002f, 5.138316e-002f, 5.687791e-002f, 6.131400e-002f, 6.442924e-002f, 6.603583e-002f, 6.603583e-002f, 6.442924e-002f, 6.131400e-002f, 5.687791e-002f, 5.138316e-002f, 4.514497e-002f, 3.850630e-002f, 3.181093e-002f, 2.537743e-002f, 1.947623e-002f, 1.431186e-002f, 1.001174e-002f, 6.622334e-003f, 4.112716e-003f, 2.384937e-003f, 1.289839e-003f, 6.466073e-004f, 2.639688e-004f },
+ { 1.230588e-004f, 4.146753e-004f, 8.667268e-004f, 1.606401e-003f, 2.759319e-003f, 4.437769e-003f, 6.729266e-003f, 9.686389e-003f, 1.331883e-002f, 1.758836e-002f, 2.240733e-002f, 2.764087e-002f, 3.311284e-002f, 3.861510e-002f, 4.391962e-002f, 4.879247e-002f, 5.300873e-002f, 5.636712e-002f, 5.870346e-002f, 5.990168e-002f, 5.990168e-002f, 5.870346e-002f, 5.636712e-002f, 5.300873e-002f, 4.879247e-002f, 4.391962e-002f, 3.861510e-002f, 3.311284e-002f, 2.764087e-002f, 2.240733e-002f, 1.758836e-002f, 1.331883e-002f, 9.686389e-003f, 6.729266e-003f, 4.437769e-003f, 2.759319e-003f, 1.606401e-003f, 8.667268e-004f, 4.146753e-004f, 1.230588e-004f }
+ },
+ {
+ { -1.296358e-003f, -5.406338e-003f, -1.238714e-002f, -1.074497e-002f, 2.042052e-002f, 9.036964e-002f, 1.784209e-001f, 2.406237e-001f, 2.406237e-001f, 1.784209e-001f, 9.036964e-002f, 2.042052e-002f, -1.074497e-002f, -1.238714e-002f, -5.406338e-003f, -1.296358e-003f },
+ { -8.259212e-004f, -3.169595e-003f, -7.490150e-003f, -1.139292e-002f, -7.615798e-003f, 1.259730e-002f, 5.317232e-002f, 1.081085e-001f, 1.616906e-001f, 1.949256e-001f, 1.949256e-001f, 1.616906e-001f, 1.081085e-001f, 5.317232e-002f, 1.259730e-002f, -7.615798e-003f, -1.139292e-002f, -7.490150e-003f, -3.169595e-003f, -8.259212e-004f },
+ { -1.354857e-003f, -4.302660e-003f, -9.432808e-003f, -1.124037e-002f, 2.250991e-003f, 4.111857e-002f, 1.028676e-001f, 1.686674e-001f, 2.114261e-001f, 2.114261e-001f, 1.686674e-001f, 1.028676e-001f, 4.111857e-002f, 2.250991e-003f, -1.124037e-002f, -9.432808e-003f, -4.302660e-003f, -1.354857e-003f },
+ { -7.776167e-004f, -1.982913e-003f, -4.025207e-003f, -6.651467e-003f, -8.336999e-003f, -6.445344e-003f, 1.958825e-003f, 1.888766e-002f, 4.426837e-002f, 7.535189e-002f, 1.070253e-001f, 1.330266e-001f, 1.477009e-001f, 1.477009e-001f, 1.330266e-001f, 1.070253e-001f, 7.535189e-002f, 4.426837e-002f, 1.888766e-002f, 1.958825e-003f, -6.445344e-003f, -8.336999e-003f, -6.651467e-003f, -4.025207e-003f, -1.982913e-003f, -7.776167e-004f },
+ { -4.275563e-004f, -1.005197e-003f, -1.862639e-003f, -3.080131e-003f, -4.529092e-003f, -5.820210e-003f, -6.316238e-003f, -5.216578e-003f, -1.703686e-003f, 4.874847e-003f, 1.482584e-002f, 2.797246e-002f, 4.359346e-002f, 6.046098e-002f, 7.697822e-002f, 9.139795e-002f, 1.020847e-001f, 1.077729e-001f, 1.077729e-001f, 1.020847e-001f, 9.139795e-002f, 7.697822e-002f, 6.046098e-002f, 4.359346e-002f, 2.797246e-002f, 1.482584e-002f, 4.874847e-003f, -1.703686e-003f, -5.216578e-003f, -6.316238e-003f, -5.820210e-003f, -4.529092e-003f, -3.080131e-003f, -1.862639e-003f, -1.005197e-003f, -4.275563e-004f },
+ { -2.035376e-004f, -6.689791e-004f, -1.328527e-003f, -2.272442e-003f, -3.481130e-003f, -4.781697e-003f, -5.833453e-003f, -6.149991e-003f, -5.158507e-003f, -2.289504e-003f, 2.916797e-003f, 1.070557e-002f, 2.102369e-002f, 3.347458e-002f, 4.732339e-002f, 6.155612e-002f, 7.498701e-002f, 8.640007e-002f, 9.470396e-002f, 9.907658e-002f, 9.907658e-002f, 9.470396e-002f, 8.640007e-002f, 7.498701e-002f, 6.155612e-002f, 4.732339e-002f, 3.347458e-002f, 2.102369e-002f, 1.070557e-002f, 2.916797e-003f, -2.289504e-003f, -5.158507e-003f, -6.149991e-003f, -5.833453e-003f, -4.781697e-003f, -3.481130e-003f, -2.272442e-003f, -1.328527e-003f, -6.689791e-004f, -2.035376e-004f },
+ { -1.515930e-004f, -2.822090e-004f, -4.354332e-004f, -6.231711e-004f, -8.542967e-004f, -1.132997e-003f, -1.457305e-003f, -1.817934e-003f, -2.197524e-003f, -2.570369e-003f, -2.902690e-003f, -3.153472e-003f, -3.275862e-003f, -3.219077e-003f, -2.930750e-003f, -2.359614e-003f, -1.458378e-003f, -1.866640e-004f, 1.486172e-003f, 3.578478e-003f, 6.094172e-003f, 9.021158e-003f, 1.233042e-002f, 1.597587e-002f, 1.989496e-002f, 2.401016e-002f, 2.823111e-002f, 3.245754e-002f, 3.658270e-002f, 4.049722e-002f, 4.409326e-002f, 4.726869e-002f, 4.993109e-002f, 5.200155e-002f, 5.341783e-002f, 5.413695e-002f, 5.413695e-002f, 5.341783e-002f, 5.200155e-002f, 4.993109e-002f, 4.726869e-002f, 4.409326e-002f, 4.049722e-002f, 3.658270e-002f, 3.245754e-002f, 2.823111e-002f, 2.401016e-002f, 1.989496e-002f, 1.597587e-002f, 1.233042e-002f, 9.021158e-003f, 6.094172e-003f, 3.578478e-003f, 1.486172e-003f, -1.866640e-004f, -1.458378e-003f, -2.359614e-003f, -2.930750e-003f, -3.219077e-003f, -3.275862e-003f, -3.153472e-003f, -2.902690e-003f, -2.570369e-003f, -2.197524e-003f, -1.817934e-003f, -1.457305e-003f, -1.132997e-003f, -8.542967e-004f, -6.231711e-004f, -4.354332e-004f, -2.822090e-004f, -1.515930e-004f },
+ { -5.038663e-005f, -1.566090e-004f, -2.776591e-004f, -4.222836e-004f, -5.979724e-004f, -8.099774e-004f, -1.060382e-003f, -1.347281e-003f, -1.664124e-003f, -1.999269e-003f, -2.335792e-003f, -2.651573e-003f, -2.919681e-003f, -3.109052e-003f, -3.185454e-003f, -3.112703e-003f, -2.854091e-003f, -2.373975e-003f, -1.639451e-003f, -6.220534e-004f, 7.006086e-004f, 2.343343e-003f, 4.312078e-003f, 6.602848e-003f, 9.201100e-003f, 1.208140e-002f, 1.520752e-002f, 1.853299e-002f, 2.200202e-002f, 2.555088e-002f, 2.910955e-002f, 3.260375e-002f, 3.595715e-002f, 3.909375e-002f, 4.194031e-002f, 4.442872e-002f, 4.649828e-002f, 4.809774e-002f, 4.918705e-002f, 4.973870e-002f, 4.973870e-002f, 4.918705e-002f, 4.809774e-002f, 4.649828e-002f, 4.442872e-002f, 4.194031e-002f, 3.909375e-002f, 3.595715e-002f, 3.260375e-002f, 2.910955e-002f, 2.555088e-002f, 2.200202e-002f, 1.853299e-002f, 1.520752e-002f, 1.208140e-002f, 9.201100e-003f, 6.602848e-003f, 4.312078e-003f, 2.343343e-003f, 7.006086e-004f, -6.220534e-004f, -1.639451e-003f, -2.373975e-003f, -2.854091e-003f, -3.112703e-003f, -3.185454e-003f, -3.109052e-003f, -2.919681e-003f, -2.651573e-003f, -2.335792e-003f, -1.999269e-003f, -1.664124e-003f, -1.347281e-003f, -1.060382e-003f, -8.099774e-004f, -5.979724e-004f, -4.222836e-004f, -2.776591e-004f, -1.566090e-004f, -5.038663e-005f }
+ }
+ };
+}
223 src/sivantoledo/ax25/Afsk1200Modulator.java
@@ -0,0 +1,223 @@
+/*
+ * Audio FSK modem for AX25 (1200 Baud, 1200/2200Hz).
+ *
+ * Copyright (C) Sivan Toledo, 2012
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package sivantoledo.ax25;
+
+//import java.util.Arrays;
+
+public class Afsk1200Modulator
+ implements PacketModulator
+ //implements HalfduplexSoundcardClient
+ {
+
+ private float phase_inc_f0, phase_inc_f1;
+ private float phase_inc_symbol;
+
+ //private Packet packet; // received packet
+ private int sample_rate;
+
+ public Afsk1200Modulator(int sample_rate) {
+ this.sample_rate = sample_rate;
+ phase_inc_f0 = (float) (2.0*Math.PI*1200.0/sample_rate);
+ phase_inc_f1 = (float) (2.0*Math.PI*2200.0/sample_rate);
+ phase_inc_symbol = (float) (2.0*Math.PI*1200.0/sample_rate);
+ }
+
+ //private float phase_f0, phase_f1;
+ //private int t; // running sample counter
+
+ //private float f1cos, f1sin, f0cos, f0sin;
+
+ /**************************/
+ /*** Packet Transmitter ***/
+ /**************************/
+
+ public void setTxDelay(int delay) { tx_delay = delay; };
+ public void setTxTail (int delay) { tx_tail = delay; };
+
+ private static enum TxState {
+ IDLE,
+ PREAMBLE,
+ DATA,
+ TRAILER
+ };
+ private TxState tx_state = TxState.IDLE;
+ private byte[] tx_bytes;
+ private int tx_index;
+ private int tx_delay = 20; // default is 20*10ms = 500ms
+ private int tx_tail = 0; // obsolete
+ private float tx_symbol_phase, tx_dds_phase;
+
+ private float[] tx_samples;
+ private int tx_last_symbol;
+ private int tx_stuff_count;
+
+ public void prepareToTransmitFlags(int seconds) {
+ if (tx_state != TxState.IDLE) {
+ System.err.println("Warning: trying to trasmit while Afsk1200 modulator is busy, discarding");
+ return;
+ }
+ tx_bytes = null; // no data
+ tx_state = TxState.PREAMBLE;
+ tx_index = (int) Math.ceil((double) seconds / (8.0/1200.0)); // number of flags to transmit
+ //if (transmit_controller!=null) transmit_controller.startTransmitter();
+ tx_symbol_phase = tx_dds_phase = 0.0f;
+ }
+
+ public void prepareToTransmit(Packet p) {
+ if (tx_state != TxState.IDLE) {
+ System.err.println("Warning: trying to trasmit while Afsk1200 modulator is busy, discarding");
+ return;
+ }
+ tx_bytes = p.bytesWithCRC(); // This includes the CRC
+ tx_state = TxState.PREAMBLE;
+ tx_index = (int) Math.ceil(tx_delay * 0.01 / (8.0/1200.0)); // number of flags to transmit
+ if (tx_index < 1) tx_index = 1;
+ //if (transmit_controller!=null) transmit_controller.startTransmitter();
+ tx_symbol_phase = tx_dds_phase = 0.0f;
+ }
+
+ public float[] getTxSamplesBuffer() {
+ if (tx_samples==null) {
+ // each byte makes up to 10 symbols,
+ // each symbol takes (1/1200)s to transmit.
+ // not sure if it's really necessary to add one.
+ tx_samples = new float[ (int) Math.ceil((10.0/1200.0) * sample_rate) + 1 ];
+ }
+ return tx_samples;
+ }
+
+ private int generateSymbolSamples(int symbol, float[] s, int position) {
+ int count = 0;
+ while (tx_symbol_phase < (float) (2.0*Math.PI)) {
+ s[position] = (float) Math.sin(tx_dds_phase);
+
+ if (symbol==0) tx_dds_phase += phase_inc_f0;
+ else tx_dds_phase += phase_inc_f1;
+
+ tx_symbol_phase += phase_inc_symbol;
+
+ //if (tx_symbol_phase > (float) (2.0*Math.PI)) tx_symbol_phase -= (float) (2.0*Math.PI);
+ if (tx_dds_phase > (float) (2.0*Math.PI)) tx_dds_phase -= (float) (2.0*Math.PI);
+
+ position++;
+ count++;
+ }
+
+ tx_symbol_phase -= (float) (2.0*Math.PI);
+
+ return count;
+ }
+
+ private int byteToSymbols(int bits, boolean stuff) {
+ int symbol;
+ int position = 0;
+ int n;
+ //System.out.printf("byte=%02x stuff=%b\n",bits,stuff);
+ for (int i=0; i<8; i++) {
+ int bit = bits & 1;
+ //System.out.println("i="+i+" bit="+bit);
+ bits = bits >> 1;
+ if (bit == 0) { // we switch sybols (frequencies)
+ symbol = (tx_last_symbol == 0) ? 1 : 0;
+ n = generateSymbolSamples(symbol, tx_samples, position);
+ position += n;
+
+ if (stuff) tx_stuff_count = 0;
+ tx_last_symbol = symbol;
+ } else {
+ symbol = (tx_last_symbol == 0) ? 0 : 1;
+ n = generateSymbolSamples(symbol, tx_samples, position);
+ position += n;
+
+ if (stuff) tx_stuff_count++;
+ tx_last_symbol = symbol;
+
+ if (stuff && tx_stuff_count==5) {
+ // send a zero
+ //System.out.println("stuffing a zero bit!");
+ symbol = (tx_last_symbol == 0) ? 1 : 0;
+ n = generateSymbolSamples(symbol, tx_samples, position);
+ position += n;
+
+ tx_stuff_count = 0;
+ tx_last_symbol = symbol;
+ }
+ }
+ }
+ //System.out.println("generated "+position+" samples");
+ return position;
+ }
+
+ public int getSamples() {
+ int count;
+
+ assert(tx_samples != null);
+
+ switch (tx_state) {
+ case IDLE:
+ return 0;
+ case PREAMBLE:
+ count = byteToSymbols(0x7E,false);
+
+ tx_index--;
+ if (tx_index==0) {
+ tx_state = TxState.DATA;
+ tx_index = 0;
+ tx_stuff_count = 0;
+ }
+ break;
+ case DATA:
+ if (tx_bytes==null) { // we just wanted to transmit tones to adjust the transmitter
+ tx_state = TxState.IDLE;
+ //if (transmit_controller!=null) transmit_controller.stopTransmitter();
+ return 0;
+ }
+ //System.out.printf("Data byte %02x\n",tx_bytes[tx_index]);
+ count = byteToSymbols(tx_bytes[tx_index],true);
+
+ tx_index++;
+ if (tx_index==tx_bytes.length) {
+ tx_state = TxState.TRAILER;
+ if (tx_tail <= 0) { // this should be the normal case
+ tx_index = 2;
+ } else {
+ tx_index = (int) Math.ceil(tx_tail * 0.01 / (8.0/1200.0)); // number of flags to transmit
+ if (tx_tail < 2) tx_tail = 2;
+ }
+ }
+ break;
+ case TRAILER:
+ count = byteToSymbols(0x7E,false);
+
+ tx_index--;
+ if (tx_index==0) {
+ tx_state = TxState.IDLE;
+ //if (transmit_controller!=null) transmit_controller.stopTransmitter();
+ }
+ break;
+ default:
+ assert(false);
+ count = -1;
+ break;
+ }
+
+ return count;
+ }
+}
109 src/sivantoledo/ax25/Afsk1200MultiDemodulator.java
@@ -0,0 +1,109 @@
+/*
+ * Audio FSK modem for AX25 (1200 Baud, 1200/2200Hz).
+ * This class combines two demodulators into one packet stream,
+ * to handle both de-emphasized and flat (discriminator) audio.
+ *
+ * Copyright (C) Sivan Toledo, 2012
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package sivantoledo.ax25;
+
+import java.util.Arrays;
+
+public class Afsk1200MultiDemodulator implements PacketDemodulator {
+
+ private class InnerHandler implements PacketHandler {
+ int d;
+ public InnerHandler(int demodulator) {
+ d = demodulator;
+ }
+ public void handlePacket(byte[] bytes) {
+ Afsk1200MultiDemodulator.this.handlePacket(bytes, d);
+ }
+
+ }
+
+ private int packet_count;
+ private long sample_count;
+ private byte[] last;
+ //private long last_sample_count;
+ private int dup_count;
+ //public void incSampleCount() { sample_count++; }
+ private int last_demod, d0_count, d6_count, both_count;
+ public void handlePacket(byte[] bytes, int d) {
+ //public void handlePacket(byte[] bytes) {
+ if (last!=null && d != last_demod && Arrays.equals(last, bytes)) {
+ //&& sample_count <= last_sample_count + max_sample_delay ) {
+ dup_count++;
+ //System.err.printf("Duplicate, %d so far\n",dup_count);
+
+ if (last_demod == 0)
+ d0_count--;
+ else
+ d6_count--;
+ both_count++;
+
+ //last_demod = d;
+ } else {
+ packet_count++;
+
+ //System.err.printf("Non duplicate, d=%d last_d=%d same-data=%b\n",d, last_demod,
+ // Arrays.equals(last, bytes));
+ //
+ //if (last!=null) {
+ // System.err.printf("lengths = %d %d\n",bytes.length,last.length);
+ // for (int i=0; i<bytes.length; i++) {
+ // if (i>=last.length) break;
+ // System.err.printf("%02x %02x\n",bytes[i],last[i]);
+ // }
+ //}
+
+ if (d == 0)
+ d0_count++;
+ else
+ d6_count++;
+
+ last_demod = d;
+ //System.out.println(""+packet_count);
+ last = Arrays.copyOf(bytes,bytes.length);
+ //last_sample_count = sample_count;
+
+ if (h!=null) h.handlePacket(bytes);
+ }
+ //System.err.printf("d0=%d d6=%d both=%d total=%d\n",d0_count,d6_count,both_count,packet_count);
+ }
+
+ private PacketHandler h;
+ PacketDemodulator d0, d6;
+ //private int sample_rate;
+ //private int max_sample_delay;
+
+ public Afsk1200MultiDemodulator(int sample_rate, PacketHandler h) throws Exception {
+ //this.sample_rate = sample_rate;
+ this.h = h;
+ //max_sample_delay = (10 * 8 * sample_rate) / 1200; // a 10 byte delay
+ d0 = new Afsk1200Demodulator(sample_rate,1,0,new InnerHandler(0));
+ d6 = new Afsk1200Demodulator(sample_rate,1,6,new InnerHandler(6));
+ }
+ public void addSamples(float[] s, int n) {
+ sample_count += n;
+ d0.addSamples(s, n);
+ d6.addSamples(s, n);
+ }
+ public boolean dcd(){
+ return d6.dcd() || d0.dcd();
+ }
+}
165 src/sivantoledo/ax25/Filter.java
@@ -0,0 +1,165 @@
+/*
+ *
+ *
+ * Copyright (C) Sivan Toledo, 2012
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package sivantoledo.ax25;
+
+public class Filter {
+ public final static float[] xxxLOWPASS_1200_48000_39 = {
+ 1.230588e-004f,
+ 4.146753e-004f,
+ 8.667268e-004f,
+ 1.606401e-003f,
+ 2.759319e-003f,
+ 4.437769e-003f,
+ 6.729266e-003f,
+ 9.686389e-003f,
+ 1.331883e-002f,
+ 1.758836e-002f,
+ 2.240733e-002f,
+ 2.764087e-002f,
+ 3.311284e-002f,
+ 3.861510e-002f,
+ 4.391962e-002f,
+ 4.879247e-002f,
+ 5.300873e-002f,
+ 5.636712e-002f,
+ 5.870346e-002f,
+ 5.990168e-002f,
+ 5.990168e-002f,
+ 5.870346e-002f,
+ 5.636712e-002f,
+ 5.300873e-002f,
+ 4.879247e-002f,
+ 4.391962e-002f,
+ 3.861510e-002f,
+ 3.311284e-002f,
+ 2.764087e-002f,
+ 2.240733e-002f,
+ 1.758836e-002f,
+ 1.331883e-002f,
+ 9.686389e-003f,
+ 6.729266e-003f,
+ 4.437769e-003f,
+ 2.759319e-003f,
+ 1.606401e-003f,
+ 8.667268e-004f,
+ 4.146753e-004f,
+ 1.230588e-004f
+ };
+
+ public final static float[] xxxBANDPASS_1150_1250_48000_39 = {
+ -7.469398e-003f,
+ -7.830087e-003f,
+ -8.975283e-003f,
+ -1.060416e-002f,
+ -1.227981e-002f,
+ -1.347975e-002f,
+ -1.365708e-002f,
+ -1.230591e-002f,
+ -9.024217e-003f,
+ -3.567459e-003f,
+ 4.112634e-003f,
+ 1.384849e-002f,
+ 2.525863e-002f,
+ 3.777121e-002f,
+ 5.066739e-002f,
+ 6.314018e-002f,
+ 7.436358e-002f,
+ 8.356498e-002f,
+ 9.009411e-002f,
+ 9.348162e-002f,
+ 9.348162e-002f,
+ 9.009411e-002f,
+ 8.356498e-002f,
+ 7.436358e-002f,
+ 6.314018e-002f,
+ 5.066739e-002f,
+ 3.777121e-002f,
+ 2.525863e-002f,
+ 1.384849e-002f,
+ 4.112634e-003f,
+ -3.567459e-003f,
+ -9.024217e-003f,
+ -1.230591e-002f,
+ -1.365708e-002f,
+ -1.347975e-002f,
+ -1.227981e-002f,
+ -1.060416e-002f,
+ -8.975283e-003f,
+ -7.830087e-003f,
+ -7.469398e-003f,
+ };
+
+ public final static float[] xxxBANDPASS_2150_2250_48000_39 = {
+ 5.961802e-003f,
+ 4.708974e-003f,
+ 3.164012e-003f,
+ 4.947263e-004f,
+ -4.027708e-003f,
+ -1.075260e-002f,
+ -1.944758e-002f,
+ -2.922208e-002f,
+ -3.860217e-002f,
+ -4.575104e-002f,
+ -4.879775e-002f,
+ -4.621260e-002f,
+ -3.715482e-002f,
+ -2.172039e-002f,
+ -1.034705e-003f,
+ 2.283710e-002f,
+ 4.715506e-002f,
+ 6.890336e-002f,
+ 8.525608e-002f,
+ 9.402762e-002f,
+ 9.402762e-002f,
+ 8.525608e-002f,
+ 6.890336e-002f,
+ 4.715506e-002f,
+ 2.283710e-002f,
+ -1.034705e-003f,
+ -2.172039e-002f,
+ -3.715482e-002f,
+ -4.621260e-002f,
+ -4.879775e-002f,
+ -4.575104e-002f,
+ -3.860217e-002f,
+ -2.922208e-002f,
+ -1.944758e-002f,
+ -1.075260e-002f,
+ -4.027708e-003f,
+ 4.947263e-004f,
+ 3.164012e-003f,
+ 4.708974e-003f,
+ 5.961802e-003f,
+ };
+
+ // filter a signal x stored in a cyclic buffer with
+ // a FIR filter f
+ // The length of x must be larger than the length of the filter.
+ public static float filter(float[] x, int j, float[] f) {
+ float c = (float) 0.0;
+ for (int i=0; i<f.length; i++) {
+ c += x[j]*f[i];
+ j--;
+ if (j==-1) j=x.length - 1;
+ }
+ return c;
+ }
+}
399 src/sivantoledo/ax25/Packet.java
@@ -0,0 +1,399 @@
+/*
+ *
+ *
+ * Copyright (C) Sivan Toledo, 2012
+ *
+ * The CRC computation code is adapted from soundmodem, Copyright (C) 1999-2000
+ * by Thomas Sailer (sailer@ife.ee.ethz.ch). That code is also released under GPL
+ * version 2 or later.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+package sivantoledo.ax25;
+
+import java.util.Arrays;
+
+public class Packet {
+
+ private final int AX25_CRC_CORRECT = 0xF0B8;
+ private final int CRC_CCITT_INIT_VAL = 0xFFFF;
+ private final int MAX_FRAME_SIZE = // not including delimiting flags
+ 7+7 // source and destination
+ +(8*7) // path
+ +1+1 // control and PID
+ +256 // information
+ +2; // frame checksum
+
+ public static final int AX25_CONTROL_APRS = 0x03;
+ public static final int AX25_PROTOCOL_COMPRESSED_TCPIP = 0x06;
+ public static final int AX25_PROTOCOL_UNCOMPRESSED_TCPIP = 0x07;
+ public static final int AX25_PROTOCOL_NO_LAYER_3 = 0xF0; // used for APRS
+
+ private final int crc_ccitt_tab[] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
+ };
+
+ private int crc = CRC_CCITT_INIT_VAL;
+ private byte packet[] = new byte[MAX_FRAME_SIZE];
+ private int size = 0;
+
+ private float[] stats;
+ public void statistics(float[] stats) {
+ this.stats = stats;
+ for (float f : stats)
+ System.err.printf("%.2f ",f);
+ System.err.println(format(Arrays.copyOf(packet, size-2)));
+ }
+ public float[] statistics() { return stats; }
+
+ //public byte[] bytes() {
+ // return Arrays.copyOf(packet, size-2); // trim the checksum
+ //}
+
+ public byte[] bytesWithCRC() {
+ return Arrays.copyOf(packet, size); // trim the checksum
+ }
+
+ public byte[] bytesWithoutCRC() {
+ return Arrays.copyOf(packet, size-2); // trim the checksum
+ }
+
+ // this constructor is used by Afsk1200 to construct empty packets for reception
+ public Packet() {
+ }
+
+ // this constructor is used for sending packets from raw bytes
+ public Packet(byte[] bytes) {
+ assert (crc == CRC_CCITT_INIT_VAL);
+ assert (bytes.length+2 <= packet.length);
+
+ for (int i=0; i<bytes.length; i++) {
+ packet[size] = bytes[i];
+ crc_ccitt_update(packet[size]);
+ size++;
+ }
+
+ int crcl = (crc & 0xff) ^ 0xff;
+ int crch = (crc >> 8) ^ 0xff;
+
+ packet[size] = (byte) crcl;
+ crc_ccitt_update(packet[size]);
+ size++;
+
+ packet[size] = (byte) crch;
+ crc_ccitt_update(packet[size]);
+ size++;
+
+ assert (crc == AX25_CRC_CORRECT);
+ }
+
+ public Packet(String destination,
+ String source,
+ String[] path,
+ int control,
+ int protocol,
+ byte[] payload) {
+ int n = 7 + 7 + 7*path.length + 2 + payload.length;
+ byte[] bytes = new byte[n];
+
+ int offset = 0;
+
+ addCall(bytes, offset, destination, false);
+ offset += 7;
+ addCall(bytes, offset, source , path==null || path.length==0);
+ offset += 7;
+ for (int i=0; i<path.length; i++) {
+ addCall(bytes, offset, path[i], i==path.length-1);
+ offset += 7;
+ }
+
+ bytes[offset++] = (byte) control;
+ //System.out.printf("control = %02x\n", bytes[offset-1]);
+ bytes[offset++] = (byte) protocol;
+ //System.out.printf("protocol = %02x\n", bytes[offset-1]);
+
+ for (int j=0; j<payload.length; j++) {
+ bytes[offset++] = payload[j];
+ //System.out.printf("data = %02x\n", bytes[offset-1]);
+ }
+
+ assert(offset == n);
+ assert(size == 0);
+
+ assert (crc == CRC_CCITT_INIT_VAL);
+ assert (bytes.length+2 <= packet.length);
+
+ for (int i=0; i<bytes.length; i++) {
+ packet[size] = bytes[i];
+ crc_ccitt_update(packet[size]);
+ size++;
+ }
+
+ int crcl = (crc & 0xff) ^ 0xff;
+ int crch = (crc >> 8) ^ 0xff;
+
+ packet[size] = (byte) crcl;
+ crc_ccitt_update(packet[size]);
+ size++;
+
+ packet[size] = (byte) crch;
+ crc_ccitt_update(packet[size]);
+ size++;
+
+ assert (crc == AX25_CRC_CORRECT);
+ }
+
+ static void addCall(byte[] bytes, int offset, String call, boolean last) {
+ int i;
+ boolean call_ended = false;
+ char c = ' ';
+ int ssid = 0;
+
+ for (i=0; i<6; i++) {
+ if (i<call.length())
+ c = call.charAt(i);
+ else
+ call_ended = true;
+ if (call_ended || !Character.isLetterOrDigit(c) || c=='-') {
+ call_ended = true;
+ c = ' ';
+ } else c = Character.toUpperCase(c);
+ bytes[offset++] = (byte) (c << 1);
+ }
+
+ for (i=0; i<call.length(); i++) {
+ c = call.charAt(i);
+ if (c=='-' && i+1<call.length()) {
+ ssid = Integer.parseInt(call.substring(i+1));
+ if (ssid > 15 || ssid < 0) ssid=0; // this is an error
+ break;
+ }
+ }
+
+ /* The low-order bit of last call SSID should be set to 1 */
+ ssid = (ssid << 1) | (0x60) | (last ? 0x01 : 0);
+ bytes[offset++] = (byte) ssid;
+ }
+
+ /*** Packet parser ***/
+
+ public String source, destination;
+ public String[] path;
+ public byte[] payload;
+
+ private static String parseCall(byte[] packet, int offset) {
+ String call = "";
+ int c, i;
+ //int size = 0;
+
+ for (i=0; i<6; i++) {
+ c = (packet[offset+i] > 0) ? packet[offset+i] >> 1 : (packet[offset+i]+256) >> 1;
+ //System.out.printf("Parsing byte %02x offset %d <%c>\n",c,offset+i,(char)c);
+ if ((char) c != ' ')
+ call += (char) c;
+ }
+
+ c = (packet[offset+i] > 0) ? packet[offset+i] >> 1 : (packet[offset+i]+256) >> 1;
+ int ssid = c & 0x0f;
+ if (ssid != 0)
+ call += String.format("-%d", ssid);
+
+ return new String(call);
+ }
+
+ public void parse() {
+ int offset= 0;
+ destination = parseCall(packet,offset);
+ offset += 7;
+ source = parseCall(packet,offset);
+ offset += 7;
+
+ int repeaters = 0;
+ while (offset+7 <= size && (packet[offset-1] & 0x01) == 0) {
+ repeaters++;
+ if (repeaters > 8) break; // missing LSB=1 to terminate the path
+ String path_element = parseCall(packet,offset);
+ offset += 7;
+ if (path == null) {
+ path = new String[1];
+ path[0] = path_element;
+ } else {