# Accela Signal -- Solution

## Overview
A LoRa-like Chirp Spread Spectrum (CSS) IQ stream containing two types of frames:
beacon (cleartext) and data (XOR-encrypted flag). Players must implement CSS
demodulation from scratch to decode the frames.

## Steps

### 1. Capture IQ Stream
Connect to TCP port 9002. A text banner appears first, followed by raw IQ data.
```bash
nc HOST 9002 > capture.raw
# Or use the solve script
```

The banner tells you: `IQ baseband, 8000 sps, int16 LE interleaved`.

### 2. Analyze the Signal
Open the IQ data in a spectrogram tool (e.g., inspectrum, Python matplotlib, or
GNU Radio). You'll see:
- Characteristic **chirp** patterns: frequency sweeps from low to high
- Repeating preambles (identical chirps)
- Gaps of noise between transmissions

This is **Chirp Spread Spectrum (CSS)**, the modulation used by LoRa.

### 3. Determine Parameters
- Each chirp spans 128 samples → **N = 128**
- Since N = 2^SF → **SF = 7** (spreading factor)
- Bandwidth = sample rate = 8000 Hz (baseband at Nyquist)
- 7 bits per symbol

### 4. Implement Dechirping
The key to CSS demodulation is **dechirping**:

1. Generate the base upchirp (symbol 0):
   ```
   x0[n] = exp(j * π * n²/N)    for n = 0..127
   ```

2. To decode a received chirp, multiply by the **conjugate** of the base chirp:
   ```
   dechirped[n] = received[n] * conj(x0[n])
   ```

3. Take the **DFT/FFT** of the dechirped signal. The peak bin = symbol value.

### 5. Detect Frames
Frame structure:
```
[Preamble: 8× symbol 0] [Sync: 2× downchirp] [Header: 1 symbol] [Payload: L symbols]
```

- **Preamble**: 8 consecutive chirps all decoding to symbol 0
- **Sync**: 2 downchirps (conjugate of upchirps)
- **Header**: 1 symbol = payload length in bytes (Gray-coded)
- **Payload**: L symbols encoding the data bytes

### 6. Gray Decoding
Symbol values are **Gray-coded** (like real LoRa). After finding the FFT peak
bin, apply inverse Gray code:
```python
def gray_decode(val):
    mask = val
    while mask:
        mask >>= 1
        val ^= mask
    return val
```

### 7. Symbol-to-Byte Unpacking
Each symbol carries 7 bits (SF=7). Concatenate all bits from decoded symbols,
then group into 8-bit bytes.

### 8. Parse Frame Payload
Payload format: `[type:1] [data:L] [crc16:2]`

- Type 0x01 = beacon (ASCII text, for verification)
- Type 0x02 = data (XOR-encrypted flag)
- CRC-16 CCITT validates the payload

### 9. Decrypt Flag
The data frame's content is XOR'd with the repeating key `"L41N"` (4 bytes).

```python
xor_key = b"L41N"
flag = bytes(b ^ xor_key[i % 4] for i, b in enumerate(encrypted_data))
```

## Key Insights
- CSS/LoRa modulation encodes data as cyclic frequency shifts of a chirp signal
- The dechirp + FFT technique converts the frequency-domain problem into a simple peak detection
- Gray coding ensures that adjacent symbols (close FFT bins) differ by only 1 bit, reducing errors
- The 7-bit symbol → 8-bit byte packing is standard for non-byte-aligned symbol sizes
- The banner hints at CSS ("Chirp Spread Spectrum detected") to point players in the right direction

## Flag
`ESPILON{4cc3l4_ch1rp_spr34d_w1r3d}`

## Author
Eun0us
