Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a2b93cf
Showing
27 changed files
with
3,929 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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(); | |||
} | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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(); | |||
} | |||
} | |||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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(); | |||
} | |||
} | |||
} | |||
} | |||
} |
Oops, something went wrong.