keskiviikko, 19. syyskuu 2018

RDS raw data sync with Python

In RDS data there is no preamble, finding RDS groups can be achieved by using checkword (+offset).

rdspi.jpg

block_offsets = (0b0011111100, 0b0110011000, 0b0101101000, 0b0110110100)
groups = ('A', 'B', 'C', 'D')

def calc_cw(bits, offset):
    x = (119, 743, 943, 779, 857, 880, 440, 220, 110, 55, 711, 959, 771, 861, 882, 441)
    crc = 0
    for i in range(len(bits)):
        if bits[i] == '1':
            crc ^= x[i]
    return crc ^ block_offsets[offset]

pi = '0110001000000011' # 0x6203 = YLE Radio Suomi
data = ""
f = open('rds-out.txt', 'r')
line = f.readline().strip()
while line != "":
    data += line
    line = f.readline().strip()
f.close()
l = len(data)
payload = ""
checkword = ""
p = 0
name = ""
blocks_found = 0
while p <= l:
    payload = data[p:p+16]
    checkword = data[p+16:p+16+10]
    if ((len(payload)) + (len(checkword)) == 26):
        for i in range(len(groups)):
            cw = calc_cw(payload, i)
            if (cw == int(checkword, 2)) == True:
                name = ""
                if payload == pi:
                    name = 'YLE Radio Suomi'
                print groups[i], payload, bin(cw)[2:].zfill(10), name
                blocks_found += 1
                break
    if blocks_found > 8:
        break
    p += 1

 

maanantai, 10. syyskuu 2018

GNU Radio embedded python block: Bin2Str

rds-p1.jpg
https://ibb.co/dhyNAU

Bin2Str - convert ascii codes 0&1 to string '0' '1'
gr embedded python block code:

import numpy as np
from gnuradio import gr

class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block

    def __init__(self):          
    gr.sync_block.__init__(
            self,
            name='Bin2Str',   # will show up in GRC
            in_sig=[np.uint8],
          out_sig=[np.uint8]
        )
    self.dstr = ""

    def work(self, input_items, output_items):
    self.dstr = input_items[0].tostring()
    self.dstr = self.dstr.replace('\x00', '0')
    self.dstr = self.dstr.replace('\x01', '1')
    output_items[0][:] = np.fromstring(self.dstr, dtype=np.uint8)
        return 1

bin2str.jpg

perjantai, 31. elokuu 2018

Strange signal at 450.675 MHz

Location: Finnland, Pori
Signal is transmitted approx. every 15th minutes.

f1.jpg
https://ibb.co/iSXHNz

f2.jpg
https://ibb.co/ftt7Nz

f.jpg
https://ibb.co/fa9E2z

c1.jpgc3.jpg

maanantai, 20. elokuu 2018

GNU Radio Log Power FFT block

gr1.jpg
https://ibb.co/jqbSfz

grp.jpg

keskiviikko, 15. elokuu 2018

POC: Pocsag rx with SX1278/Arduino

HW: Dragino Lora shield & Arduino Uno. SX1278 continuous mode (https://cdn-shop.adafruit.com/product-files/3179/sx1276_77_78_79.pdf) This is not full decoder, just simple preamble/frame sync/idle codeword detector.

// http://wiki.dragino.com/index.php?title=Lora_Shield

#include <SPI.h>

#define SERIAL_SPEED        57600
#define BPS                 512
#define FREQ                450.002
#define STATE_SLEEP         0
#define STATE_STANDBY       1
#define STATE_FSRX          4
#define STATE_RX            5
#define S_DELAY             10
#define L_DELAY             100
#define SX_BITRATE          "BPS "
#define SX_FREQ             "Frequency "
#define SX_VERSION          "Version code of the chip "
#define PREAMBLE_TXT        "Preamble 0xAAAAAAAA"
#define FRAME_SYNC_TXT      "Frame sync 0x7CD215D8"
#define IDLE_CODEWORD_TXT   "Idle 0x7A89C197"
#define RSSI_THRESHOLD      160             // -rssi/2
#define CH_FILTER_BW        B00000100       // 25 kHz

#define DCLK_PIN            6
#define DATA_PIN            7

#define RegFifo             0x00
#define RegOpMode           0x01
#define RegBitrateMsb       0x02
#define RegBitrateLsb       0x03
#define RegFrfMsb           0x06
#define RegFrfMid           0x07
#define RegFrfLsb           0x08
#define RegPaRamp           0x0a
#define RegRssiConfig       0x0e
#define RegRssiThresh       0x10
#define RegRxBw             0x12
#define RegOokPeak          0x14
#define RegPreambleDetect   0x1f
#define RegSyncConfig       0x27
#define RegPacketConfig1    0x30
#define RegPacketConfig2    0x31
#define RegPayloadLength    0x32
#define RegIrqFlags2        0x3f
#define RegDioMapping1      0x40
#define RegVersion          0x42

#define SX_OSC_FREQ         32000000
#define SX_RESET            5
#define SX_RESET_DELAY_H    1
#define SX_RESET_DELAY_L    25
#define SPI_SS              10
#define SPI_RW_DELAY        50

#define FRAME_SYNC          0x7CD215D8
#define IDLE_CODEWORD       0x7A89C197
#define PREAMBLE            0xAAAAAAAA

uint32_t bitBuffer = 0;
uint8_t  bitCounter = 0;

void setup() {
  Serial.begin(SERIAL_SPEED);
  if (initSX()) {
    pinMode(DATA_PIN, INPUT);
    pinMode(DCLK_PIN, INPUT);
    Serial.print(F(SX_VERSION));
    Serial.print(readSPI(RegVersion));
    Serial.println();
    delay(S_DELAY);
    startFSK();
    uint8_t b = readSPI(RegOpMode);
    bitClear(b, 6);
    bitClear(b, 5);
    writeSPI(RegOpMode, b);

    // Channel filter bandwidth control
    writeSPI(RegRxBw, CH_FILTER_BW);

    setFreq(FREQ);
    Serial.print(F(SX_FREQ));
    Serial.println(FREQ, DEC);
    setBps(BPS);
    Serial.print(F(SX_BITRATE));
    Serial.println(BPS);

    // Bits 7-6: AutoRestartRxMode, 01  On, without waiting for the PLL to re-lock
    // Bit 4: Enables the Sync word generation and detection: 0 =>  Off, 1 =>  On
    // Bit 5: Sets the polarity of the Preamble. 0  0xAA, 1  0x55
    // Bits 2-0: Size of the Sync word (SyncSize + 1)
    b = readSPI(RegSyncConfig);
    bitClear(b, 7);
    bitSet(b, 6);
    bitClear(b, 4);
    bitClear(b, 5);
    writeSPI(RegSyncConfig, b);

    // Bits 6-5: Defines DC-free encoding/decoding performed: 00->none, 01->manchester, 10->whitening
    // Bit 7: packet format, 0 = fixed length, 1 = variable length
    // Bit 4: crc calc/check, 0 = off, 1 = on
    // Bits 2-1: Defines address based filtering in Rx: 00  None (Off)
    // Bit 3: Defines the behavior of the packet handler when CRC check fails:
    // 0 => Clear FIFO and restart new packet reception. No PayloadReady interrupt issued.
    // 1 =>  Do not clear FIFO. PayloadReady interrupt issued.
    // Bit 0: Selects the CRC and whitening algorithms:
    // 0  CCITT CRC implementation with standard whitening
    // 1  IBM CRC implementation with alternate whitening
    b = 0;
    bitSet(b, 7);
    writeSPI(RegPacketConfig1, b);

    // Bits 6-5: FSK data shaping:
    // 00 -> no shaping, 01 -> Gaussian filter BT = 1.0
    // 10 -> Gaussian filter BT = 0.5, 11 -> Gaussian filter BT = 0.3
    // Bits 3-0: Rise/Fall time of ramp up/down in FSK:
    // 1001  40 us
    b = readSPI(RegPaRamp);
    bitClear(b, 6);
    bitClear(b, 5);
    bitSet(b, 3);
    bitClear(b, 2);
    bitClear(b, 1);
    bitSet(b, 0);
    writeSPI(RegPaRamp, b);

    writeSPI(RegPayloadLength, 64);

    // Data processing mode: 0 => Continuous mode, 1 => Packet mode
    b = readSPI(RegPacketConfig2);
    bitClear(b, 6);
    writeSPI(RegPacketConfig2, b);
    
    // Set DIO mapping
    b = readSPI(RegDioMapping1);
    bitClear(b, 5);
    bitClear(b, 4);
    bitClear(b, 3);
    bitClear(b, 2);
    writeSPI(RegDioMapping1, b);

    // RSSI smoothing.
    // Defines the number of samples taken to average the RSSI result. 010 -> 8 samples
    b = readSPI(RegRssiConfig);
    bitClear(b, 2);
    bitSet(b, 1);
    bitClear(b, 0);
    writeSPI(RegRssiConfig, b);

    writeSPI(RegRssiThresh, RSSI_THRESHOLD);

    // Bit 5: enables the Bit Synchronizer:
    // 0 -> bit sync disabled (not possible in packet mode), 1 -> bit sync enabled
    b = readSPI(RegOokPeak);
    bitSet(b, 5);
    writeSPI(RegOokPeak, b);

    // RegPreambleDetect (0x1f). Enables Preamble detector when set to 1.
    // The AGC settings supersede this bit during the startup / AGC phase.
    // Bit 7: 0 -> turned off, 1 -> turned on
    // Bits 6-5: Number of Preamble bytes to detect to trigger an interrupt.
    // 00  1 byte, 10  3 bytes, 01  2 bytes
    b = readSPI(RegPreambleDetect);
    bitClear(b, 7);
    bitSet(b, 6);
    bitClear(b, 5);
    writeSPI(RegPreambleDetect, b);

    clearFifoAndFlags();

    digitalWrite(DCLK_PIN, LOW);

    setState(STATE_FSRX);
    delay(L_DELAY);
    setState(STATE_RX);
    delay(S_DELAY);
  }
}

void loop() {
  bitWrite(bitBuffer, 0, (PIND & B10000000));
  bitCounter++;
  if (bitCounter != 32) {
    bitBuffer = bitBuffer << 1;
  }
  else {
    bitCounter = 0;
    switch (bitBuffer) {
      case PREAMBLE:
        Serial.println(F(PREAMBLE_TXT));
        break;
      case FRAME_SYNC:
        Serial.println(F(FRAME_SYNC_TXT));
        break;
      case IDLE_CODEWORD:
        Serial.println(F(IDLE_CODEWORD_TXT));
        break;
    }
    bitBuffer = 0;
  }
}

void clearFifoAndFlags() {
  // Flag(s) and FIFO are cleared when this bit is set
  uint8_t b = readSPI(RegIrqFlags2);
  bitSet(b, 4);
  writeSPI(RegIrqFlags2, b);
  delay(S_DELAY);
}

void setBps(uint16_t bps) {
  uint16_t baudRate = SX_OSC_FREQ / bps;
  writeSPI(RegBitrateMsb, baudRate >> 8);
  writeSPI(RegBitrateLsb, baudRate & 0xff);
}

void setFreq(float f)
{
  uint32_t lf = (f * 1000000) / 61.035;
  writeSPI(RegFrfMsb, (lf >> 16) & 0xff);
  writeSPI(RegFrfMid, (lf >> 8) & 0xff);
  writeSPI(RegFrfLsb, lf & 0xff);
}

void setState(uint8_t s) {
  uint8_t b = readSPI(RegOpMode);
  b = b | s;
  writeSPI(RegOpMode, b);
}

void startFSK()
{
  setState(STATE_SLEEP);
  delay(S_DELAY);
  uint8_t b = readSPI(RegOpMode);
  bitClear(b, 7);
  writeSPI(RegOpMode, b);
}

bool initSX() {
  pinMode(SX_RESET, OUTPUT);
  digitalWrite(SX_RESET, LOW);
  initSPI();
  resetSX();
  if (readSPI(RegVersion) == 0) {
    return false;
  }
  setState(STATE_STANDBY);
  return true;
}

void resetSX() {
  digitalWrite(SX_RESET, HIGH);
  delay(SX_RESET_DELAY_H);
  digitalWrite(SX_RESET, LOW);
  delay(SX_RESET_DELAY_L);
}

uint8_t readSPI(uint8_t addr) {
  digitalWrite(SPI_SS, LOW);
  SPI.transfer(addr);
  delayMicroseconds(SPI_RW_DELAY);
  uint8_t v = SPI.transfer(0x00);
  digitalWrite(SPI_SS, HIGH);
  return v;
}

void writeSPI(uint8_t addr, uint8_t v) {
  digitalWrite(SPI_SS, LOW);
  SPI.transfer(addr | 0x80);
  delayMicroseconds(SPI_RW_DELAY);
  SPI.transfer(v);
  digitalWrite(SPI_SS, HIGH);
}

void initSPI()
{
  digitalWrite(SPI_SS, HIGH);
  pinMode(SPI_SS, OUTPUT);  
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
}