Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
Thomas Kolb | 778ac7f815 | ||
Thomas Kolb | 5848d78272 | ||
Thomas Kolb | 0b48995cec | ||
Thomas Kolb | 0309c042e4 | ||
Thomas Kolb | 159c3f1518 | ||
Thomas Kolb | 1ae421e876 | ||
Thomas Kolb | a455fac050 | ||
Thomas Kolb | 44921bd65d | ||
Thomas Kolb | ccd9af6664 | ||
Thomas Kolb | 4dc2c60c8b | ||
Thomas Kolb | 706a1eb437 | ||
Thomas Kolb | eb802629a1 | ||
Thomas Kolb | c61a7a7cf7 |
|
@ -3,7 +3,7 @@ jobs:
|
||||||
build-doc:
|
build-doc:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
container:
|
container:
|
||||||
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.6
|
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.7
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: cd doc && make
|
- run: cd doc && make
|
||||||
|
@ -16,7 +16,7 @@ jobs:
|
||||||
runs-on: docker
|
runs-on: docker
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
container:
|
container:
|
||||||
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.6
|
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.7
|
||||||
steps:
|
steps:
|
||||||
- run: mkdir ~/.ssh && echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 && chmod 0600 ~/.ssh/id_ed25519 && echo "${{ secrets.SSH_KNOWN_HOST }}" > ~/.ssh/known_hosts
|
- run: mkdir ~/.ssh && echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 && chmod 0600 ~/.ssh/id_ed25519 && echo "${{ secrets.SSH_KNOWN_HOST }}" > ~/.ssh/known_hosts
|
||||||
- uses: actions/download-artifact@v3
|
- uses: actions/download-artifact@v3
|
||||||
|
|
|
@ -6,7 +6,7 @@ RUN apt update && apt install -y --no-install-recommends nodejs git ruby-rubygem
|
||||||
RUN gem install asciidoctor asciidoctor-diagram
|
RUN gem install asciidoctor asciidoctor-diagram
|
||||||
|
|
||||||
# tools for diagram generation
|
# tools for diagram generation
|
||||||
RUN apt install -y --no-install-recommends mscgen && apt clean
|
RUN apt install -y --no-install-recommends mscgen graphviz && apt clean
|
||||||
|
|
||||||
# tools for automatic deployment
|
# tools for automatic deployment
|
||||||
RUN apt install -y --no-install-recommends rsync openssh-client && apt clean
|
RUN apt install -y --no-install-recommends rsync openssh-client && apt clean
|
||||||
|
|
|
@ -10,7 +10,7 @@ Thomas Kolb DL5TKL
|
||||||
|
|
||||||
== Introduction
|
== Introduction
|
||||||
|
|
||||||
Hamnet70 intends to provide a network system in the 70 cm amateur radio band that can transfer Internet Protocol (IP) packets at a high speed.
|
https://git.tkolb.de/amateurfunk/hamnet70[Hamnet70] intends to provide a network system in the 70 cm amateur radio band that can transfer Internet Protocol (IP) packets at a high speed.
|
||||||
The protocols defined here are inspired by the Packet Radio network and Bluetooth Low Energy.
|
The protocols defined here are inspired by the Packet Radio network and Bluetooth Low Energy.
|
||||||
|
|
||||||
The protocols are designed primarily for centralized infrastructure as they were common in the Packet Radio network.
|
The protocols are designed primarily for centralized infrastructure as they were common in the Packet Radio network.
|
||||||
|
@ -238,6 +238,49 @@ Due to the long possible bursts, this system can achieve high throughput if ther
|
||||||
|
|
||||||
=== Frame Definitions
|
=== Frame Definitions
|
||||||
|
|
||||||
|
==== Data Frame
|
||||||
|
|
||||||
|
Data Frames carry all higher-layer data in a connection.
|
||||||
|
Each Data Frame transfers a single layer-3 packet.
|
||||||
|
|
||||||
|
The layer 2 header of Data Frames is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `000` (Data Frame)
|
||||||
|
- TX Request: `1` if this is the last packet in the burst, `0` otherwise
|
||||||
|
- Source Address: the transmitter’s HAM-64 address
|
||||||
|
- Destination Address: the target station’s HAM-64 address
|
||||||
|
- TX sequence number: as required by Go-Back-N
|
||||||
|
- RX sequence number: as required by Go-Back-N
|
||||||
|
|
||||||
|
To identify how the encoded packet should be handled, the layer 3 protocol is encoded in the first byte of the layer 2 payload.
|
||||||
|
The full layer 2 payload therefore is composed as follows:
|
||||||
|
|
||||||
|
- Layer 3 protocol ID (1 Byte)
|
||||||
|
- Layer 3 packet data (variable length)
|
||||||
|
|
||||||
|
So far, the following protocols are defined and supported:
|
||||||
|
|
||||||
|
[cols="2,1,1", options="header"]
|
||||||
|
.Layer 3 protocol identifiers. EtherType is given as reference.
|
||||||
|
|===
|
||||||
|
| Protocol | Hamnet70 ID | EtherType
|
||||||
|
|
||||||
|
| IPv6
|
||||||
|
| `0x00`
|
||||||
|
| `0x86DD`
|
||||||
|
|
||||||
|
| IPv4
|
||||||
|
| `0x10`
|
||||||
|
| `0x0800`
|
||||||
|
|
||||||
|
| _undefined/auto_
|
||||||
|
| `0xFF`
|
||||||
|
| -
|
||||||
|
|
||||||
|
3+|_All other values are reserved._
|
||||||
|
|
||||||
|
|===
|
||||||
|
|
||||||
==== Empty Frame
|
==== Empty Frame
|
||||||
|
|
||||||
The Empty Frame does not contain any data and therefore only consists of the header and the CRC.
|
The Empty Frame does not contain any data and therefore only consists of the header and the CRC.
|
||||||
|
@ -254,15 +297,255 @@ They therefore do not have a _TX sequence number_; this field is reserved and mu
|
||||||
|
|
||||||
The _RX sequence number_ and all other header fields are used as usual.
|
The _RX sequence number_ and all other header fields are used as usual.
|
||||||
|
|
||||||
=== Ideas
|
==== Connection Management
|
||||||
|
|
||||||
To be defined:
|
Connection Management Frames control the Layer 2 connection between a client and the digipeater.
|
||||||
|
They are especially important for connect and disconnect procedures.
|
||||||
|
|
||||||
- connection establishment procedure (request, response)
|
Connection Management Frames have at least one byte in the data field: the type of the management packet.
|
||||||
- how do clients get an IP(v6) address? -> should be derived from the call sign
|
The following types are defined:
|
||||||
- when are new clients allowed to connect? -> base station calls for any new stations in regular intervals
|
|
||||||
- handling of packets from unknown clients that are not connection requests
|
[cols="1,2,5", options="header"]
|
||||||
- signal quality handling
|
.Connection Management Types
|
||||||
|
|===
|
||||||
|
|Type Indicator
|
||||||
|
|Sent by
|
||||||
|
|Description
|
||||||
|
|
||||||
|
|`0x00`
|
||||||
|
|Digipeater
|
||||||
|
|Beacon
|
||||||
|
|
||||||
|
|`0x01`
|
||||||
|
|Client
|
||||||
|
|Connection Request
|
||||||
|
|
||||||
|
|`0x02`
|
||||||
|
|Digipeater
|
||||||
|
|Connection Parameters
|
||||||
|
|
||||||
|
|`0x03`
|
||||||
|
|Digipeater
|
||||||
|
|Connection Reset
|
||||||
|
|
||||||
|
|`0x04`
|
||||||
|
|Digipeater
|
||||||
|
|Disconnect Request
|
||||||
|
|
||||||
|
|`0x05`
|
||||||
|
|Client
|
||||||
|
|Disconnect
|
||||||
|
|===
|
||||||
|
|
||||||
|
===== Beacons
|
||||||
|
|
||||||
|
Beacons are sent periodically by the digipeater to show it’s presence to potential new clients.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the HAM-64 broadcast address
|
||||||
|
- TX sequence number: reserved, always 0
|
||||||
|
- RX sequence number: reserved, always 0
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x00` to indicate that this is a Beacon packet.
|
||||||
|
|
||||||
|
A Beacon packet shall always be sent as the last or only packet in a burst.
|
||||||
|
After a Beacon is sent by the digipeater it listens for a short time for Connection Requests from new clients.
|
||||||
|
A new client that intends to connect shall send the Connection Request as soon as possible after the beacon transmission ends.
|
||||||
|
|
||||||
|
===== Connection Request
|
||||||
|
|
||||||
|
A Connection Request is sent by a client that intends to connect to a digipeater.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the new client’s HAM-64 address
|
||||||
|
- Destination Address: the digipeater’s HAM-64 address
|
||||||
|
- TX sequence number: reserved, always 0
|
||||||
|
- RX sequence number: reserved, always 0
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x01` to indicate that this is a Connection Request packet.
|
||||||
|
|
||||||
|
A Connection Request is always a response to a Beacon packet.
|
||||||
|
It shall be the only packet in a burst.
|
||||||
|
|
||||||
|
When the digipeater receives a Connection Request it has two options for a response:
|
||||||
|
|
||||||
|
. Accept the connection by sending a Connection Parameters packet to the client in the next burst.
|
||||||
|
. Reject the connection by sending a Connection Reset packet to the client in the next burst.
|
||||||
|
|
||||||
|
===== Connection Parameters
|
||||||
|
|
||||||
|
The Connection Parameters packet has two functions: it indicates to a client that its Connection Request was accepted and tells it how to configure its network stack.
|
||||||
|
This is the first packet in the regular Go-back-N data flow.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the new client’s HAM-64 address
|
||||||
|
- TX sequence number: 0 (the first packet)
|
||||||
|
- RX sequence number: 0 (no packets received yet)
|
||||||
|
|
||||||
|
The type indicator is `0x02`.
|
||||||
|
|
||||||
|
After the type indicator follow one or more configuration blocks.
|
||||||
|
Each block consists of three data fields:
|
||||||
|
|
||||||
|
. Configuration Type: 1 byte
|
||||||
|
. Configuration Data Length: 1 byte
|
||||||
|
. Configuration data: variable number of bytes as indicated by Configuration Data Length
|
||||||
|
|
||||||
|
The following Configuration Types are defined:
|
||||||
|
|
||||||
|
[cols="1,1,5", options="header"]
|
||||||
|
.Configuration Types
|
||||||
|
|===
|
||||||
|
|Type Indicator
|
||||||
|
|Length
|
||||||
|
|Description
|
||||||
|
|
||||||
|
|`0x00`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 address
|
||||||
|
|
||||||
|
|`0x01`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 gateway
|
||||||
|
|
||||||
|
|`0x02`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 DNS server (may appear multiple times)
|
||||||
|
|
||||||
|
|`0x03` - `0x07`
|
||||||
|
|-
|
||||||
|
|_reserved_
|
||||||
|
|
||||||
|
|`0x08`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 address
|
||||||
|
|
||||||
|
|`0x09`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 gateway
|
||||||
|
|
||||||
|
|`0x0A`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 DNS server (may appear multiple times)
|
||||||
|
|
||||||
|
|`0x0B` - `0xFF`
|
||||||
|
|-
|
||||||
|
|_reserved_
|
||||||
|
|
||||||
|
|===
|
||||||
|
|
||||||
|
If the client receives an unknown Configuration Type the corresponding block shall be skipped.
|
||||||
|
The remaining blocks shall be parsed as usual.
|
||||||
|
|
||||||
|
===== Connection Reset
|
||||||
|
|
||||||
|
A Connection Reset is set by the digipeater if it receives unexpected packets from stations that are not connected.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1` if this is the last packet in the burst, `0` otherwise
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the new client’s HAM-64 address
|
||||||
|
- TX sequence number: reserved, always 0
|
||||||
|
- RX sequence number: reserved, always 0
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x03` to indicate that this is a Connection Reset packet.
|
||||||
|
|
||||||
|
A Connection Request is sent in a regular burst and can be in any position.
|
||||||
|
|
||||||
|
This message means that the digipeater does not have any information about a connection with the addressed client.
|
||||||
|
Therefore, when a client receives a Connection Reset, it shall drop its complete connection state and start a new connection procedure if desired.
|
||||||
|
|
||||||
|
===== Disconnect Request
|
||||||
|
|
||||||
|
A Disconnect Request is set by the digipeater when it wants to orderly shut down a client connection.
|
||||||
|
It is the last packet that the digipeater sends in a connection.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1` if this is the last packet in the burst, `0` otherwise
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the client’s HAM-64 address
|
||||||
|
- TX sequence number: as required by Go-Back-N
|
||||||
|
- RX sequence number: as required by Go-Back-N
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x04` to indicate that this is a Disconnect Request packet.
|
||||||
|
|
||||||
|
A Disconnect Request is sent in a regular burst and can be in any position.
|
||||||
|
The sequence numbers are sent and handled the same way as in regular connection packets.
|
||||||
|
|
||||||
|
When a client receives a Disconnect Request, it may transmit the remainder of its current queue, but must not queue new data packets.
|
||||||
|
After the last packet is transmitted, the client shall send a Disconnect packet, which confirms the end of the connection.
|
||||||
|
|
||||||
|
The digipeater must keep the connection state in memory until either the Disconnect packet is received from the client or the connection times out.
|
||||||
|
|
||||||
|
===== Disconnect
|
||||||
|
|
||||||
|
A Disconnect packet is sent by the client to terminate the connection.
|
||||||
|
It is the last packet in a connection and is not confirmed by the digipeater.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `0`
|
||||||
|
- Source Address: the client’s HAM-64 address
|
||||||
|
- Destination Address: the digipeater’s HAM-64 address
|
||||||
|
- TX sequence number: as required by Go-Back-N
|
||||||
|
- RX sequence number: as required by Go-Back-N
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x05` to indicate that this is a Disconnect packet.
|
||||||
|
|
||||||
|
A Disconnect is sent always as the final packet of a burst.
|
||||||
|
The sequence numbers are sent and handled the same way as in regular connection packets.
|
||||||
|
|
||||||
|
When the digipeater receives a disconnect packet in the regular packet flow (i.e. no previous packets are lost), it will immediately drop the connection state and not call this client again.
|
||||||
|
Therefore, if the client wants to ensure that all previous packets are transmitted, it must wait until the digipeater confirms that by sending the corresponding RX sequence number before sending the Disconnect packet.
|
||||||
|
|
||||||
|
==== Connectionless Frame
|
||||||
|
|
||||||
|
Connectionless Frames are used to transfer packets between unconnected notes.
|
||||||
|
They can be used to implement custom protocols (similar to APRS, which is implemented on top of AX.25).
|
||||||
|
|
||||||
|
The layer 2 header of Connectionless Frames is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `100` (Connectionless Frame)
|
||||||
|
- TX Request: `1` if this is the last packet in the burst, `0` otherwise
|
||||||
|
- Source Address: the transmitter’s HAM-64 address
|
||||||
|
- Destination Address: the target station’s HAM-64 address
|
||||||
|
- TX sequence number: user-defined
|
||||||
|
- RX sequence number: user-defined
|
||||||
|
|
||||||
|
The sequence numbers can be used in any way that is useful for the custom protocol.
|
||||||
|
|
||||||
|
It is required that the first byte of each Connectionless Frame identify the protocol being used.
|
||||||
|
Protocol numbers are centrally assigned and are listed below.
|
||||||
|
Some protocol numbers are reserved for experimentation and development and can be self-assigned temporarily.
|
||||||
|
|
||||||
|
[cols="1,3", options="header"]
|
||||||
|
.Connectionless Frame protocol IDs
|
||||||
|
|===
|
||||||
|
|Protocol IDs
|
||||||
|
|Description
|
||||||
|
|
||||||
|
|`0x00 .. 0xF7`
|
||||||
|
|_reserved_
|
||||||
|
|
||||||
|
|`0xF8 .. 0xFF`
|
||||||
|
|Available for experimentation and developmentfootnote:[If you are developing a new protocol, you can freely pick a number from this range. Please check which temporary IDs are already used around your location and pick a free one. When your protocol reaches a sufficiently stable state, please request an official ID assignment].
|
||||||
|
|===
|
||||||
|
|
||||||
=== Message Sequence Charts
|
=== Message Sequence Charts
|
||||||
|
|
||||||
|
@ -290,7 +573,7 @@ msc {
|
||||||
digi box client [label="Connection established"];
|
digi box client [label="Connection established"];
|
||||||
|
|
||||||
--- [label="Alternative 2: Connection is rejected"];
|
--- [label="Alternative 2: Connection is rejected"];
|
||||||
digi -> client [label="Connection Refusal"];
|
digi -> client [label="Connection Reset"];
|
||||||
}
|
}
|
||||||
....
|
....
|
||||||
|
|
||||||
|
@ -373,6 +656,75 @@ msc {
|
||||||
}
|
}
|
||||||
....
|
....
|
||||||
|
|
||||||
|
=== State Diagrams
|
||||||
|
|
||||||
|
This section documents state diagrams for the digipeater and the clients.
|
||||||
|
They are only informational; different implementations are possible and allowed as long as the procedures defined by the <<_message_sequence_charts,message sequence charts>> are obeyed.
|
||||||
|
However, the state diagrams shown here reflect the reference implementation.
|
||||||
|
|
||||||
|
==== Connection State Diagram
|
||||||
|
|
||||||
|
[graphviz,format=svg,svg-type=interactive,scale=1.0]
|
||||||
|
.Connection state diagram
|
||||||
|
....
|
||||||
|
digraph connection {
|
||||||
|
uninitd [label="Uninitialized"];
|
||||||
|
connecting [label="Connecting"];
|
||||||
|
wait_client [label="Wait for\nclient"];
|
||||||
|
estab [label="Two-way communication\nestablished"];
|
||||||
|
closed [label="Closed"];
|
||||||
|
|
||||||
|
uninitd -> connecting [label="Connection request\nsent [client]"];
|
||||||
|
uninitd -> wait_client [label="Connection request\nreceived [digipeater]"];
|
||||||
|
|
||||||
|
connecting -> estab [label="Connection\nacknowledgement sent"];
|
||||||
|
wait_client -> estab [label="Connection\nacknowledged"];
|
||||||
|
|
||||||
|
connecting -> closed [label="Timeout"];
|
||||||
|
wait_client -> closed [label="Timeout"];
|
||||||
|
|
||||||
|
estab -> closed [label="Disconnect/Timeout"];
|
||||||
|
closed -> uninitd [label="State erased"];
|
||||||
|
}
|
||||||
|
....
|
||||||
|
|
||||||
|
==== Digipeater Connection Multiplexing
|
||||||
|
|
||||||
|
[graphviz,layout=neato,format=svg,svg-type=interactive,scale=1.0]
|
||||||
|
.Handling of multiple connections at the digipeater
|
||||||
|
....
|
||||||
|
digraph multiclient {
|
||||||
|
overlap=false;
|
||||||
|
|
||||||
|
idle [label="Idle"];
|
||||||
|
send_beacon [label="Send Beacon"];
|
||||||
|
accept_new [label="Check for\nnew clients"];
|
||||||
|
check_conn [label="Check connection timers"];
|
||||||
|
send_to_client [label="Send queued packets\nto client"];
|
||||||
|
receive_from_client [label="Receive packets\nfrom client"];
|
||||||
|
check_timeout [label="Check connection\ntimeout"];
|
||||||
|
reset_timeout [label="Reset connection\ntimeout"];
|
||||||
|
close_conn [label="Close\nconnection"];
|
||||||
|
|
||||||
|
idle -> send_beacon [label="Beacon timeout\nexpired"];
|
||||||
|
send_beacon -> accept_new;
|
||||||
|
accept_new -> idle;
|
||||||
|
|
||||||
|
idle -> check_conn [label="Any contact\ntimer expired"];
|
||||||
|
check_conn -> send_to_client [label="Contact timer\nexpired"];
|
||||||
|
send_to_client -> receive_from_client;
|
||||||
|
receive_from_client -> reset_timeout [label="Packets received"];
|
||||||
|
reset_timeout -> check_conn;
|
||||||
|
|
||||||
|
receive_from_client -> check_timeout [label="Nothing received"];
|
||||||
|
check_timeout -> check_conn [label="No timeout yet"];
|
||||||
|
check_timeout -> close_conn [label="Timed out"];
|
||||||
|
close_conn -> check_conn;
|
||||||
|
|
||||||
|
check_conn -> idle [label="All expired\nconnections handled"];
|
||||||
|
}
|
||||||
|
....
|
||||||
|
|
||||||
== Higher Layer Protocols
|
== Higher Layer Protocols
|
||||||
|
|
||||||
[appendix]
|
[appendix]
|
||||||
|
|
|
@ -143,7 +143,7 @@ float freq_est_in_rampup(const float complex *recv, size_t n, float *final_phase
|
||||||
|
|
||||||
// rotate every second symbol by 180°. As a plausibility check that we are
|
// rotate every second symbol by 180°. As a plausibility check that we are
|
||||||
// in fact inside the ramp-up, we verify that there is no phase rotation by
|
// in fact inside the ramp-up, we verify that there is no phase rotation by
|
||||||
// more than 90° between the symbols.
|
// more than 60° between the symbols.
|
||||||
for(size_t i = 0; i < n; i++) {
|
for(size_t i = 0; i < n; i++) {
|
||||||
if((i % 2) == 0) {
|
if((i % 2) == 0) {
|
||||||
rotated[i] = recv[i];
|
rotated[i] = recv[i];
|
||||||
|
@ -154,7 +154,7 @@ float freq_est_in_rampup(const float complex *recv, size_t n, float *final_phase
|
||||||
float phase = cargf(rotated[i]);
|
float phase = cargf(rotated[i]);
|
||||||
if(i > 0) {
|
if(i > 0) {
|
||||||
float phase_delta = phase - prev_phase;
|
float phase_delta = phase - prev_phase;
|
||||||
if(fabsf(phase_delta) > 3.14159f/2.0f) {
|
if(fabsf(phase_delta) > 3.14159f/3.0f) {
|
||||||
// abort because we either have the wrong signal or too much noise.
|
// abort because we either have the wrong signal or too much noise.
|
||||||
if(final_phase) {
|
if(final_phase) {
|
||||||
*final_phase = 0.0f;
|
*final_phase = 0.0f;
|
||||||
|
|
|
@ -33,9 +33,11 @@
|
||||||
#define HEADER_SIZE_BYTES 2
|
#define HEADER_SIZE_BYTES 2
|
||||||
#define FREQ_EST_L 24
|
#define FREQ_EST_L 24
|
||||||
|
|
||||||
#define AGC_BW_ACQUISITION 5e-2f
|
#define AGC_BW_ACQUISITION 3e-2f
|
||||||
#define AGC_BW_TRACKING 1e-4f
|
#define AGC_BW_TRACKING 1e-4f
|
||||||
|
|
||||||
|
#define SQUELCH_TIMEOUT_MS 2
|
||||||
|
|
||||||
#define MAX_COARSE_FREQ_OFFSET 0.20f
|
#define MAX_COARSE_FREQ_OFFSET 0.20f
|
||||||
|
|
||||||
#define PLL_BW_HEADER 0.03f
|
#define PLL_BW_HEADER 0.03f
|
||||||
|
@ -206,7 +208,7 @@ static enum squelch_state_t update_and_check_squelch(layer1_rx_t *rx, unsigned i
|
||||||
// Adjustment value is in dB.
|
// Adjustment value is in dB.
|
||||||
rx->noise_floor_level += 1e-4f;
|
rx->noise_floor_level += 1e-4f;
|
||||||
}
|
}
|
||||||
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 10.0f); // in dB
|
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 6.0f); // in dB
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
||||||
|
@ -216,15 +218,15 @@ static enum squelch_state_t update_and_check_squelch(layer1_rx_t *rx, unsigned i
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
||||||
|
case LIQUID_AGC_SQUELCH_FALL:
|
||||||
|
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
||||||
result = SQUELCH_OPEN;
|
result = SQUELCH_OPEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_FALL:
|
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
||||||
LOG(LVL_DEBUG, "Squelch enabled at #%zu RSSI = %.3f dB [thr: %.3f dB]", rx->sample_index, level, agc_crcf_squelch_get_threshold(rx->agc));
|
LOG(LVL_DEBUG, "Squelch enabled at #%zu RSSI = %.3f dB [thr: %.3f dB]", rx->sample_index, level, agc_crcf_squelch_get_threshold(rx->agc));
|
||||||
// fall through
|
// fall through
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
|
||||||
case LIQUID_AGC_SQUELCH_ENABLED:
|
case LIQUID_AGC_SQUELCH_ENABLED:
|
||||||
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
|
||||||
result = SQUELCH_CLOSED;
|
result = SQUELCH_CLOSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -239,13 +241,13 @@ static bool is_squelch_open(const layer1_rx_t *rx)
|
||||||
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
||||||
case LIQUID_AGC_SQUELCH_RISE:
|
case LIQUID_AGC_SQUELCH_RISE:
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
||||||
|
case LIQUID_AGC_SQUELCH_FALL:
|
||||||
|
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_FALL:
|
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
|
||||||
case LIQUID_AGC_SQUELCH_ENABLED:
|
|
||||||
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
||||||
|
case LIQUID_AGC_SQUELCH_ENABLED:
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -265,6 +267,8 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
static float complex samples2dump_s[8192];
|
static float complex samples2dump_s[8192];
|
||||||
static size_t nsamples2dump_s = 0;
|
static size_t nsamples2dump_s = 0;
|
||||||
|
|
||||||
|
static float evm;
|
||||||
|
|
||||||
// cache configuration flags
|
// cache configuration flags
|
||||||
bool is_central_node = options_is_flag_set(OPTIONS_FLAG_IS_CENTRAL_NODE);
|
bool is_central_node = options_is_flag_set(OPTIONS_FLAG_IS_CENTRAL_NODE);
|
||||||
|
|
||||||
|
@ -344,6 +348,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
// go on with decoding the header
|
// go on with decoding the header
|
||||||
rx->state = RX_STATE_HEADER;
|
rx->state = RX_STATE_HEADER;
|
||||||
symbol_counter = 0;
|
symbol_counter = 0;
|
||||||
|
evm = 0.0f;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -361,6 +366,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
LOG(LVL_DUMP, "@%zu: Sym: %d; Phase error: %f %s", rx->sample_index, sym_demod, phase_error,
|
LOG(LVL_DUMP, "@%zu: Sym: %d; Phase error: %f %s", rx->sample_index, sym_demod, phase_error,
|
||||||
(fabs(phase_error) > 0.3) ? "!!!" : "");
|
(fabs(phase_error) > 0.3) ? "!!!" : "");
|
||||||
|
|
||||||
|
evm += modem_get_demodulator_evm(rx->hdr_demod);
|
||||||
|
|
||||||
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_HEADER);
|
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_HEADER);
|
||||||
|
|
||||||
|
@ -378,7 +384,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
// store debug info about the header
|
// store debug info about the header
|
||||||
rx->packet_debug_info.noise_floor_level = rx->noise_floor_level;
|
rx->packet_debug_info.noise_floor_level = rx->noise_floor_level;
|
||||||
rx->packet_debug_info.header_rssi = agc_crcf_get_rssi(rx->agc);
|
rx->packet_debug_info.header_rssi = agc_crcf_get_rssi(rx->agc);
|
||||||
rx->packet_debug_info.header_evm = -1e38f; // FIXME
|
rx->packet_debug_info.header_evm = evm / symbol_counter;
|
||||||
|
|
||||||
ERR_CHECK_LIQUID(liquid_repack_bytes(
|
ERR_CHECK_LIQUID(liquid_repack_bytes(
|
||||||
symbols_int, modem_get_bps(rx->hdr_demod), rx->hdr_len_symbols,
|
symbols_int, modem_get_bps(rx->hdr_demod), rx->hdr_len_symbols,
|
||||||
|
@ -433,6 +439,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
|
|
||||||
rx->state = RX_STATE_DATA;
|
rx->state = RX_STATE_DATA;
|
||||||
symbol_counter = 0;
|
symbol_counter = 0;
|
||||||
|
evm = 0.0f;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -453,6 +460,8 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
float phase_error = modem_get_demodulator_phase_error(rx->payload_demod);
|
float phase_error = modem_get_demodulator_phase_error(rx->payload_demod);
|
||||||
//LOG(LVL_DEBUG, "@%zu: Sym: %d; Phase error: %f", rx->sample_index, sym_demod, phase_error);
|
//LOG(LVL_DEBUG, "@%zu: Sym: %d; Phase error: %f", rx->sample_index, sym_demod, phase_error);
|
||||||
|
|
||||||
|
evm += modem_get_demodulator_evm(rx->payload_demod);
|
||||||
|
|
||||||
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_DATA);
|
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_DATA);
|
||||||
|
|
||||||
symbols_int[symbol_counter] = sym_demod;
|
symbols_int[symbol_counter] = sym_demod;
|
||||||
|
@ -471,7 +480,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
|
|
||||||
// store debug info about the data
|
// store debug info about the data
|
||||||
rx->packet_debug_info.data_rssi = agc_crcf_get_rssi(rx->agc);
|
rx->packet_debug_info.data_rssi = agc_crcf_get_rssi(rx->agc);
|
||||||
rx->packet_debug_info.data_evm = -1e38f; // FIXME
|
rx->packet_debug_info.data_evm = evm / symbol_counter;
|
||||||
|
|
||||||
// deinterleave the message symbols
|
// deinterleave the message symbols
|
||||||
uint8_t symbols_int_deinterleaved[rx->payload_len_symbols];
|
uint8_t symbols_int_deinterleaved[rx->payload_len_symbols];
|
||||||
|
@ -553,6 +562,7 @@ result_t layer1_rx_init(layer1_rx_t *rx, rx_callback_t callback)
|
||||||
rx->noise_floor_level = 0.0f;
|
rx->noise_floor_level = 0.0f;
|
||||||
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 3.0f); // in dB
|
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 3.0f); // in dB
|
||||||
agc_crcf_squelch_enable(rx->agc);
|
agc_crcf_squelch_enable(rx->agc);
|
||||||
|
agc_crcf_squelch_set_timeout(rx->agc, SQUELCH_TIMEOUT_MS * SYMBOL_RATE * RRC_SPS / 1000);
|
||||||
|
|
||||||
// create NCOs for carrier frequency compensation
|
// create NCOs for carrier frequency compensation
|
||||||
rx->carrier_coarse_nco = nco_crcf_create(LIQUID_NCO);
|
rx->carrier_coarse_nco = nco_crcf_create(LIQUID_NCO);
|
||||||
|
|
|
@ -60,9 +60,9 @@ void connection_destroy(connection_ctx_t *ctx);
|
||||||
|
|
||||||
/*!\brief Handle a received packet.
|
/*!\brief Handle a received packet.
|
||||||
*
|
*
|
||||||
* \param ctx The receiver context.
|
* \param ctx The connection context.
|
||||||
* \param buf Where to write the encoded packet data.
|
* \param buf Pointer to the packet data.
|
||||||
* \param buf_len Space available in the buffer.
|
* \param buf_len Length of the packet.
|
||||||
* \returns A result code from the packet handling procedure.
|
* \returns A result code from the packet handling procedure.
|
||||||
*/
|
*/
|
||||||
result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, size_t buf_len);
|
result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, size_t buf_len);
|
||||||
|
|
Loading…
Reference in a new issue