Skip to content

Commit

Permalink
Over the air testing is working
Browse files Browse the repository at this point in the history
  • Loading branch information
brucemack committed Feb 15, 2024
1 parent f0239f1 commit 770b691
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 31 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ ADC performance is not critical, this pin can be connected to digital ground.
# Used to send UDP packets. The printf command supports non-printable.
printf 'Hello\rWorld' | nc -u -w1 192.168.8.210 5198

# Rig Integration Notes

## Baofeng BF-F8HP HT

* 3.5mm Jack
- Tip - NC
- Ring - Rig mic in, MicroLink audio out
- Sleeve - PTT when pulled to ground
* 2.5mm Jack
- Tip - Rig speaker+ out, MicroLink audio in
- Ring - Rig ground
- Sleeve - Rig ground

References
==========

Expand All @@ -246,11 +259,13 @@ References
- [MCP4725 DAC](https://ww1.microchip.com/downloads/en/devicedoc/22039d.pdf)
- [Audio Amp](https://www.ti.com/lit/ds/symlink/lm4862.pdf?HQS=dis-dk-null-digikeymode-dsf-pf-null-wwe&ts=1707335785542&ref_url=https%253A%252F%252Fwww.ti.com%252Fgeneral%252Fdocs%252Fsuppproductinfo.tsp%253FdistId%253D10%2526gotoUrl%253Dhttps%253A%252F%252Fwww.ti.com%252Flit%252Fgpn%252Flm4862)
- [TLV9161 opamp suggested by Dan](https://www.ti.com/lit/ds/symlink/tlv9161.pdf?ts=1707743140015&ref_url=https%253A%252F%252Fwww.google.de%252F)
- [NTE3086 Optoisolator](https://www.nteinc.com/specs/3000to3099/pdf/nte3086.pdf)
* Rig Integration
- [Microphone Connector Reference](https://www.secradio.org.za/zs6src/secfiles/pdf/mic_soc_info.pdf)
- [Baofeng Cable Reference](https://ics-ctrl.com/cable-pinout-baofeng-ht/)
* Analog/Audio
- [Good article from Analog Devices about noise in mixed-signal systems](https://www.analog.com/media/en/analog-dialogue/volume-46/number-2/articles/staying_well_grounded.pdf)
- [Detailed article about audio/rig integration](https://www.zs2ez.co.za/Soundcard/Soundcard.htm)
* Other
- [Analog Devices Filter Wizard](https://tools.analog.com/en/filterwizard/)
- https://www.purevpn.com/what-is-vpn/protocols/openvpn
Expand Down
49 changes: 43 additions & 6 deletions src/contexts/I2CAudioOutputContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,36 @@
* FOR AMATEUR RADIO USE ONLY.
* NOT FOR COMMERCIAL USE WITHOUT PERMISSION.
*/
#include <cmath>
#include <cmath>

#include "hardware/i2c.h"

#include "common.h"
#include "UserInfo.h"
#include "I2CAudioOutputContext.h"

namespace kc1fsz {

// How long we wait in silence before deciding that the audio
// output has stopped.
static const uint32_t SQUELCH_INTERVAL_MS = 1000;

I2CAudioOutputContext::I2CAudioOutputContext(uint32_t frameSize, uint32_t sampleRate,
uint32_t bufferDepthLog2, int16_t* audioBuf)
uint32_t bufferDepthLog2, int16_t* audioBuf,
UserInfo* userInfo)
: AudioOutputContext(frameSize, sampleRate),
_bufferDepthLog2(bufferDepthLog2),
_audioBuf(audioBuf),
_userInfo(userInfo),
_frameWriteCount(0),
_framePlayCount(0),
_playPtr(0),
_intervalUs(125),
_idleCount(0),
_overflowCount(0),
_playing(false)
_playing(false),
_squelchOpen(false),
_lastAudioTime(0)
{
_timer.setIntervalUs(_intervalUs);
_dacAddr = 0x60;
Expand Down Expand Up @@ -66,8 +77,10 @@ void I2CAudioOutputContext::reset() {
_inTone = false;
_toneCount = 0;
_toneStep = 0;
_squelchOpen = false;
_lastAudioTime = 0;
_timer.reset();
// Park at middle of range
// Park DAC at middle of range
_play(0);
}

Expand Down Expand Up @@ -109,6 +122,7 @@ bool I2CAudioOutputContext::run() {

if (activity) {
if (_inTone) {

// TODO: HAVE A GAIN SETTING
_play((_toneBuf[_tonePtr >> 2]) >> 2);
// Move across tone, looking for wrap
Expand All @@ -120,7 +134,7 @@ bool I2CAudioOutputContext::run() {
_toneCount--;
if (_toneCount == 0) {
_inTone = false;
// Park at middle of range
// Park DAC at middle of range
_play(0);
}
}
Expand Down Expand Up @@ -152,11 +166,19 @@ bool I2CAudioOutputContext::run() {
} else {
_playing = false;
_idleCount++;
// Park at middle of range
// Park DAC at middle of range
_play(0);
}
}
}

// Manage squelch off
if (_squelchOpen &&
(time_ms() - _lastAudioTime) > SQUELCH_INTERVAL_MS ) {
_squelchOpen = false;
_userInfo->setSquelchOpen(_squelchOpen);
}

return activity;
}

Expand Down Expand Up @@ -192,12 +214,27 @@ static void makeFrame1(uint8_t* buf, uint16_t data) {
buf[1] = data & 0xff;
}

void I2CAudioOutputContext::_openSquelchIfNecessary() {
if (!_squelchOpen) {
_squelchOpen = true;
_userInfo->setSquelchOpen(_squelchOpen);
}
}

void I2CAudioOutputContext::_play(int16_t sample) {

if (sample != 0) {
_openSquelchIfNecessary();
}

uint16_t centeredSample = (sample + 32767);
uint8_t buf[3];
makeFrame0(buf, centeredSample);
// TODO: MAKE THIS NON-BLOCKING
i2c_write_blocking(i2c_default, _dacAddr, buf, 3, true);

// Keep track of time for squelch purposes
_lastAudioTime = time_ms();
}

}
12 changes: 11 additions & 1 deletion src/contexts/I2CAudioOutputContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

namespace kc1fsz {

class UserInfo;

/**
* An implementation of the AudioOutputContext that assumes a
* MCP4725 DAC on an I2C interface using the Pi Pico SDK.
Expand All @@ -43,7 +45,8 @@ class I2CAudioOutputContext : public AudioOutputContext {
* @param audioBuf Must be bufferDepth x frameSize in length.
*/
I2CAudioOutputContext(uint32_t frameSize, uint32_t sampleRate,
uint32_t bufferDepthLog2, int16_t* audioBuf);
uint32_t bufferDepthLog2, int16_t* audioBuf,
UserInfo* userInfo);
virtual ~I2CAudioOutputContext();

virtual void reset();
Expand All @@ -60,11 +63,13 @@ class I2CAudioOutputContext : public AudioOutputContext {
private:

void _play(int16_t pcm);
void _openSquelchIfNecessary();

uint32_t _bufferDepthLog2;
// How deep we should get before triggering the audio play
uint32_t _triggerDepth;
int16_t *_audioBuf;
UserInfo* _userInfo;
uint32_t _frameWriteCount;
uint32_t _framePlayCount;
uint32_t _playPtr;
Expand All @@ -79,6 +84,11 @@ class I2CAudioOutputContext : public AudioOutputContext {
// This indicates whether we are actually playing sound
// vs. sitting in silence.
bool _playing;
// Used to control squelch reporting
bool _squelchOpen;
// The last time we saw audio which can be used to
// manage squelch.
uint32_t _lastAudioTime;

// Tone features
static const uint32_t _toneBufSize = 20;
Expand Down
2 changes: 1 addition & 1 deletion src/contexts/PicoAudioInputContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class PicoAudioInputContext : public Runnable {
int16_t _dcBias = -95;
// This includes x16 for 12 to 16 bit PCM conversion and a gain
// of 0.5.
int16_t _gain = 15;
int16_t _gain = 16;

bool _keyed = false;

Expand Down
21 changes: 9 additions & 12 deletions src/machines/QSOFlowMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ static const uint32_t RTCP_PORT = 5199;
static const uint32_t KEEP_ALIVE_INTERVAL_MS = 10000;
// How long we wait in silence before unkeying on TX
static const uint32_t TX_TAIL_INTERVAL_MS = 1000;
// How long we wait in silence before deciding that the remote
// station has unkeyed.
static const uint32_t SQUELCH_INTERVAL_MS = 1000;

int QSOFlowMachine::traceLevel = 0;

Expand All @@ -77,7 +74,7 @@ void QSOFlowMachine::start() {
_lastRxAudioTime = 0;
_lastRxAudioSeq = 0;
_audioSeqErrors = 0;
_squelchOpen = false;
//_squelchOpen = false;
_stopRequested = false;
_state = State::OPEN_RX;
_gsmEncoder.reset();
Expand Down Expand Up @@ -105,10 +102,10 @@ void QSOFlowMachine::_processRXReceive(const UDPReceiveEvent* evt) {

_lastRxAudioTime = time_ms();

if (!_squelchOpen) {
_squelchOpen = true;
_userInfo->setSquelchOpen(_squelchOpen);
}
//if (!_squelchOpen) {
// _squelchOpen = true;
// _userInfo->setSquelchOpen(_squelchOpen);
//}

// Unload the GSM frames from the RTP packet
uint16_t remoteSeq = 0;
Expand Down Expand Up @@ -260,10 +257,10 @@ void QSOFlowMachine::_processEvent(const Event* ev) {
}
else if (ev->getType() == TickEvent::TYPE) {
// Timeout on squelch?
if (_squelchOpen && time_ms() - _lastRxAudioTime > SQUELCH_INTERVAL_MS) {
_squelchOpen = false;
_userInfo->setSquelchOpen(_squelchOpen);
}
//if (_squelchOpen && time_ms() - _lastRxAudioTime > SQUELCH_INTERVAL_MS) {
// _squelchOpen = false;
// _userInfo->setSquelchOpen(_squelchOpen);
//}
}

// Now deal with state-specific activity
Expand Down
2 changes: 1 addition & 1 deletion src/machines/QSOFlowMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ class QSOFlowMachine : public StateMachine {
uint16_t _lastRxAudioSeq;

// Indicates whether the system is actively receiving.
bool _squelchOpen;
//bool _squelchOpen;

// Buffer for outbound audio
static const uint32_t _txAudioBufDepth = 2;
Expand Down
17 changes: 14 additions & 3 deletions tests/TestUserInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "kc1fsz-tools/AudioOutputContext.h"

#include "../src/common.h"
#include "../src/UserInfo.h"

namespace kc1fsz {
Expand All @@ -20,24 +21,34 @@ class TestUserInfo : public UserInfo {
virtual void setSquelchOpen(bool sq) {

bool unkey = _squelch == true && sq == false;

_squelch = sq;

std:: cout << "UserInfo: Squelch: " << _squelch << std::endl;

// Short beep on unkey
//if (unkey) {
// if (_audioOutCtx != 0) {
// _audioOutCtx->tone(400, 75);
// }
//}

if (unkey) {
if (_audioOutCtx != 0) {
_audioOutCtx->tone(400, 75);
}
_lastSquelchCloseTime = time_ms();
}
}

bool getSquelch() const { return _squelch; }

uint32_t getMsSinceLastSquelchClose() const {
return time_ms() - _lastSquelchCloseTime;
}

private:

bool _squelch = false;
AudioOutputContext* _audioOutCtx = 0;
uint32_t _lastSquelchCloseTime = 0;
};

}
Expand Down
Loading

0 comments on commit 770b691

Please sign in to comment.